Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61043dea1b | |||
| 44a7049860 | |||
| 7dfd3cdc6c | |||
| c026c8930b |
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"phase": 1,
|
||||
"phase": 3,
|
||||
"stage": "complete",
|
||||
"milestone": "v0.3",
|
||||
"phase_role": "execution",
|
||||
"project": "atelier",
|
||||
"attempts": 0,
|
||||
"updated_at": "2026-08-05T03:25:00Z",
|
||||
"phase_tag": "v0.2.1",
|
||||
"release_id": 469
|
||||
"updated_at": "2026-08-05T03:45:00Z",
|
||||
"phase_tag": "v0.2.3",
|
||||
"release_id": 471
|
||||
}
|
||||
@@ -86,8 +86,8 @@ NFR milestone: no separate minor tag. The final patch (v0.1.5) IS the v0.2 deliv
|
||||
|-------|------|------|--------|------------------|
|
||||
| 0 | Pre-Execution | docs | complete | Spec, clarify, research, ideate, plan, PERSONAS.md (extends platform-engineer, adds ml-engineer) — shipped v0.2.0 |
|
||||
| 1 | GitOps + Operators Domain | docs | complete | domains/gitops-operators/{first-principles, argocd, flux, operators, progressive-delivery}.md — shipped v0.2.1 |
|
||||
| 2 | AI/ML Domain | docs | pending | domains/ai-ml/{first-principles, data-versioning, model-evaluation, serving, monitoring-drift}.md |
|
||||
| 3 | i18n + Compliance Domains | docs | pending | domains/i18n/{first-principles, locale-resources, formatting, rtl-bidi, testing-i18n}.md, domains/compliance/{first-principles, audit-logs, data-retention, policy-as-code, evidence}.md |
|
||||
| 2 | AI/ML Domain | docs | complete | domains/ai-ml/{first-principles, data-versioning, model-evaluation, serving, monitoring-drift}.md — shipped v0.2.2 |
|
||||
| 3 | i18n + Compliance Domains | docs | complete | domains/i18n/{first-principles, locale-resources, formatting, rtl-bidi, testing-i18n}.md, domains/compliance/{first-principles, audit-logs, data-retention, policy-as-code, evidence}.md — shipped v0.2.3 |
|
||||
| 4 | Matrix + Review Integration | docs | pending | matrix/principles-matrix.md (+40 mappings), matrix/domain-coverage.md (incl. C-rule coverage table update), review/{agent-checklist, peer-review-checklist, anti-patterns}.md, MANIFEST.md (+ examples/ listing per ATELIER-91) |
|
||||
| 5 | Examples + Cross-Links | docs | pending | examples/good + examples/bad for 4 domains, cross-links to devops/security/observability/data/k8s/iac |
|
||||
| 6 | Final Review + Ship | docs | pending | Review passed, audit clean, milestone merged to main, tag v0.2.6 |
|
||||
|
||||
+35
-5
@@ -36,13 +36,43 @@
|
||||
| DevOps | ✓ | ci-cd, environments |
|
||||
| Infrastructure as Code | ✓ | terraform, opentofu, state, modules |
|
||||
| Kubernetes | ✓ | workloads, networking, storage, rbac, helm, kustomize |
|
||||
| GitOps + Operators | ✓ | argocd, flux, operators, progressive-delivery |
|
||||
| AI / ML | ✓ | data-versioning, model-evaluation, serving, monitoring-drift |
|
||||
| i18n | ✓ | locale-resources, formatting, rtl-bidi, testing-i18n |
|
||||
| Compliance | ✓ | audit-logs, data-retention, policy-as-code, evidence |
|
||||
|
||||
## Examples
|
||||
|
||||
> Examples are illustrative markdown with fenced code only (no standalone runtime artifacts per D-020 / D-025). The `examples/` directory listing closes the v0.2 ESC-002 drift (IDEATE-17, ATELIER-91). P5 examples populate this section.
|
||||
|
||||
| Path | Status | Notes |
|
||||
|------|--------|-------|
|
||||
| `examples/good/` | (pending P5) | Good-example directory — to be populated by v0.3 P5 |
|
||||
| `examples/bad/` | (pending P5) | Bad-example directory — to be populated by v0.3 P5 |
|
||||
| `examples/good/api-endpoint.md` | (pending P5) | v0.1 example — to be listed when P5 back-fills |
|
||||
| `examples/good/react-component.md` | (pending P5) | v0.1 example — to be listed when P5 back-fills |
|
||||
| `examples/good/db-schema.md` | (pending P5) | v0.1 example — to be listed when P5 back-fills |
|
||||
| `examples/good/error-handler.md` | (pending P5) | v0.1 example — to be listed when P5 back-fills |
|
||||
| `examples/bad/god-object.md` | (pending P5) | v0.1 example — to be listed when P5 back-fills |
|
||||
| `examples/bad/silent-error.md` | (pending P5) | v0.1 example — to be listed when P5 back-fills |
|
||||
| `examples/bad/leaky-abstraction.md` | (pending P5) | v0.1 example — to be listed when P5 back-fills |
|
||||
| `examples/good/terraform-module.md` | (pending P5) | v0.2 example — to be listed when P5 back-fills |
|
||||
| `examples/good/k8s-deployment.md` | (pending P5) | v0.2 example — to be listed when P5 back-fills |
|
||||
| `examples/bad/terraform-unlocked-state.md` | (pending P5) | v0.2 example — to be listed when P5 back-fills |
|
||||
| `examples/bad/k8s-bare-pod-no-resources.md` | (pending P5) | v0.2 example — to be listed when P5 back-fills |
|
||||
| `examples/good/gitops-pr.md` | (pending P5) | v0.3 example — to be authored in P5 |
|
||||
| `examples/good/ai-ml-reproducibility.md` | (pending P5) | v0.3 example — to be authored in P5 |
|
||||
| `examples/bad/i18n-string-concat.md` | (pending P5) | v0.3 example — to be authored in P5 |
|
||||
| `examples/bad/compliance-audit-log.md` | (pending P5) | v0.3 example — to be authored in P5 |
|
||||
|
||||
> **Note:** The `examples/` section structure is established here (P4) so P5 can populate it. Files marked "pending P5" do not yet exist; they will be authored in Phase 5 and promoted from `pending` to `✓` upon completion. Listing them here as `pending` makes the manifest authoritative about what *will* exist and prevents drift.
|
||||
|
||||
## Cross-Cutting
|
||||
|
||||
| Document | Purpose |
|
||||
|-----------------------------------|----------------------------------|
|
||||
| `matrix/principles-matrix.md` | Maps domain → core principles (13 domains, 130 P-rules post-v0.2) |
|
||||
| `matrix/domain-coverage.md` | Maps core → domains; per-domain coverage |
|
||||
| `review/agent-checklist.md` | Pre-completion agent checklist (incl. IaC + k8s triggers) |
|
||||
| `review/peer-review-checklist.md` | Human peer-review checklist (incl. IaC + k8s sections) |
|
||||
| `review/anti-patterns.md` | Catalog of violations (incl. IaC + k8s + chaos anti-patterns) |
|
||||
| `matrix/principles-matrix.md` | Maps domain → core principles (17 domains, 170 P-rules post-v0.3) |
|
||||
| `matrix/domain-coverage.md` | Maps core → domains; per-domain coverage (incl. v0.3 Core Principle Coverage) |
|
||||
| `review/agent-checklist.md` | Pre-completion agent checklist (incl. IaC + k8s + gitops + ai-ml + i18n + compliance triggers) |
|
||||
| `review/peer-review-checklist.md` | Human peer-review checklist (incl. IaC + k8s + gitops + ai-ml + i18n + compliance sections) |
|
||||
| `review/anti-patterns.md` | Catalog of violations (incl. IaC + k8s + gitops + ai-ml + i18n + compliance + v0.3 chaos anti-patterns) |
|
||||
@@ -0,0 +1,165 @@
|
||||
# Audit Logs — Derived Rules
|
||||
|
||||
> Derives from `domains/compliance/first-principles.md`. Covers P1
|
||||
> (Audit Logs are Append-Only), P2 (Every Significant Action is
|
||||
> Logged), P7 (Identity is Attributable), P9 (Secrets Redacted in
|
||||
> Audit), and P10 (Compliance Posture Observable). Referenced by
|
||||
> `data-retention.md` (retention applies to audit logs themselves)
|
||||
> and `evidence.md` (audit logs are evidence).
|
||||
|
||||
## Audit Logs are Append-Only (P1 Audit Logs are Append-Only)
|
||||
|
||||
- An audit record is immutable once written. The storage substrate
|
||||
enforces this; policy alone does not. Write-once, append-only
|
||||
sinks (WORM buckets, immutable log streams, hash-chained ledgers)
|
||||
are the mechanism.
|
||||
- Deletion or mutation of an audit record is itself an auditable
|
||||
incident. The tampering is the signal, not just the underlying
|
||||
event. A system that allows `DELETE FROM audit_log` is a system
|
||||
whose audit log is a draft.
|
||||
- The append-only guarantee is testable: attempt to write, then
|
||||
attempt to overwrite, then attempt to delete. If the overwrite or
|
||||
delete succeeds, the guarantee is absent and the design is a
|
||||
violation.
|
||||
|
||||
## Structured Audit Events (P2 Every Significant Action is Logged)
|
||||
|
||||
- The set of auditable actions is defined a priori, in code, before
|
||||
the action ships. The catalog is versioned and reviewed. An
|
||||
auditable action with no log line is a violation, not a gap to
|
||||
backfill later.
|
||||
- Audit events are structured (JSON / protobuf / a typed schema),
|
||||
not prose. A prose log line ("user logged in") is unqueryable and
|
||||
unaggregatable; a structured event is both. The event schema is
|
||||
the contract between the producer and the audit pipeline.
|
||||
|
||||
```
|
||||
{
|
||||
"timestamp": "2024-11-07T15:03:22Z",
|
||||
"event": "auth.login",
|
||||
"actor": { "kind": "user", "id": "u_8f3a", "session": "s_12b9" },
|
||||
"action": "succeeded",
|
||||
"target": { "kind": "service", "id": "billing-api" },
|
||||
"source": { "ip": "203.0.113.42", "region": "us-east-1" },
|
||||
"request_id": "req_91c2",
|
||||
"version": "audit-schema/v2"
|
||||
}
|
||||
```
|
||||
|
||||
- The catalog of significant actions typically includes:
|
||||
authentication (success and failure), authorization decisions
|
||||
(allow and deny), data access (read, write, delete), configuration
|
||||
changes, policy changes, retention executions, and admin
|
||||
operations. The exact set is declared per system; the discipline
|
||||
is that it is declared.
|
||||
|
||||
## Cloud Audit Log Conventions (Prior Art, Abstracted)
|
||||
|
||||
- AWS CloudTrail, Google Cloud Audit Logs, and Azure Activity Log
|
||||
share a common shape: immutable, time-ordered, queryable, with
|
||||
actor / action / target / source / result fields. Atelier's
|
||||
audit-logs doc adopts the shape, not the vendor.
|
||||
- The shape is the contract; the sink is the implementation. A
|
||||
self-hosted audit log that follows the same shape composes with
|
||||
the same tooling (SIEM, query engines, evidence exporters) as the
|
||||
cloud vendors'.
|
||||
|
||||
## Queryability (P10 Compliance Posture Observable)
|
||||
|
||||
- An audit log that cannot be queried is an audit log that cannot be
|
||||
used. Queryability is a first-class design goal: the event schema
|
||||
is typed, fields are indexed, and the common queries (who acted on
|
||||
what when, what failed, what was denied) are cheap.
|
||||
- "Who did X between T1 and T2" must be a single query, not a
|
||||
forensics project. If the query requires a custom script per
|
||||
investigation, the audit log is structured for storage, not for
|
||||
use — a C7 (Observability) violation.
|
||||
|
||||
## Identity is Attributable (P7 Identity is Attributable)
|
||||
|
||||
- Every audit event records the authenticated principal that acted —
|
||||
not a shared account, not a generic service, not "admin." The
|
||||
actor field is populated at the time of the action from the
|
||||
authenticated session, not resolved after the fact.
|
||||
- A shared account in the actor field breaks accountability: an
|
||||
event attributed to `svc-deploy` could be any of ten engineers.
|
||||
This is the compliance angle on `domains/security/authorization.md`
|
||||
and `domains/kubernetes/rbac.md`: bind actions to unique
|
||||
principals, not to roles many can assume.
|
||||
- Machine-to-machine actions record the workload identity (a service
|
||||
account, a signed instance identity), not a human — but the
|
||||
identity is still unique and attributable to a deployable unit.
|
||||
|
||||
## Redaction at the Boundary (P9 Secrets Redacted in Audit)
|
||||
|
||||
- Audit logs must not leak secrets, credentials, tokens, or PII.
|
||||
Redaction is structural: applied at the logging boundary, before
|
||||
the record is written to the append-only sink — not opportunistic
|
||||
scrubbing after the fact. Once a secret is in an append-only log,
|
||||
the remediation is expensive (rotate, rewrite access scope), so
|
||||
redaction-at-source is the only sound position.
|
||||
- The redaction policy is itself auditable: which fields are
|
||||
redacted, by what rule, in which event type. A redaction rule
|
||||
that lives in someone's head is a P9 violation waiting to happen.
|
||||
|
||||
```
|
||||
// before redaction (DO NOT LOG)
|
||||
{
|
||||
"event": "config.read",
|
||||
"target": { "kind": "secret", "id": "db-password" },
|
||||
"value": "p@ssw0rd-plaintext-leaked" // VIOLATION
|
||||
}
|
||||
// after structural redaction
|
||||
{
|
||||
"event": "config.read",
|
||||
"target": { "kind": "secret", "id": "db-password" },
|
||||
"value": "[REDACTED:secret]",
|
||||
"redaction": "secret-value-policy/v1"
|
||||
}
|
||||
```
|
||||
|
||||
- Never log request bodies, response bodies, headers like
|
||||
`Authorization`, or environment variables that may carry secrets.
|
||||
Log the *fact* of the action, not the *content* of the secret.
|
||||
|
||||
## Retention of Audit Logs Themselves
|
||||
|
||||
- Audit logs are subject to retention policy (cross `data-
|
||||
retention.md`), but the floor is set by the accountability need,
|
||||
not by storage economy. An audit log deleted before its retention
|
||||
period is a P1 violation dressed as a P3 action.
|
||||
- The retention rule for audit logs is itself logged (meta-audit):
|
||||
when an audit log segment ages out and is deleted, the deletion is
|
||||
recorded in a higher-tier audit log with the rule that authorized
|
||||
it. The chain is observable end to end.
|
||||
|
||||
## What Violates Audit-Log Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Audit log on a mutable filesystem with no write-once protection | P1 Audit Logs are Append-Only |
|
||||
| `DELETE FROM audit_log WHERE timestamp < ...` as routine cleanup | P1 Audit Logs are Append-Only |
|
||||
| An auth-success event with no audit record | P2 Every Significant Action is Logged |
|
||||
| A prose log line ("user did a thing") instead of a structured event | P2 Every Significant Action is Logged |
|
||||
| A shared `admin` account as the actor in audit events | P7 Identity is Attributable |
|
||||
| An `Authorization: Bearer <token>` header logged in plaintext | P9 Secrets and Sensitive Data are Redacted in Audit |
|
||||
| A redaction rule applied inconsistently across event types | P9 Secrets and Sensitive Data are Redacted in Audit |
|
||||
| "Who did X?" requires a custom forensics script per investigation | P10 Compliance Posture is Observable |
|
||||
| An audit log segment deleted with no meta-audit record | P1 Audit Logs are Append-Only |
|
||||
|
||||
## Relationship to Other Domains
|
||||
|
||||
- `domains/observability/logging.md` — audit logs are structured
|
||||
logging with an append-only guarantee; the logging primitives
|
||||
(levels, structured fields, correlation IDs) compose here.
|
||||
- `domains/security/authorization.md` — the actor in an audit event
|
||||
is the principal the authorization layer authenticated.
|
||||
- `domains/security/secrets.md` — redaction at the logging boundary
|
||||
is the audit-side complement of secret management.
|
||||
- `domains/compliance/data-retention.md` — retention policy applies
|
||||
to audit logs; the audit log's own deletion is meta-audited.
|
||||
- `domains/compliance/evidence.md` — audit logs are a primary
|
||||
evidence artifact; the append-only guarantee is what makes them
|
||||
admissible.
|
||||
- `domains/kubernetes/rbac.md` — workload identity in audit events
|
||||
derives from the RBAC principal that acted.
|
||||
@@ -0,0 +1,159 @@
|
||||
# Data Retention — Derived Rules
|
||||
|
||||
> Derives from `domains/compliance/first-principles.md`. Covers P3
|
||||
> (Retention is Policy, Not Storage) and the data-shape angle on P8
|
||||
> (Subject Access is Honored). Referenced by `audit-logs.md`
|
||||
> (retention applies to audit logs) and `evidence.md` (evidence has
|
||||
> a retention lifecycle). Framework-agnostic per D-024 — no
|
||||
> regulation-specific retention periods.
|
||||
|
||||
## Retention is Policy, Not Storage (P3 Retention is Policy, Not Storage)
|
||||
|
||||
- Data lifetime is declared and enforced as policy, in code — not
|
||||
left to the storage layer's defaults. The policy names what data
|
||||
class is retained for how long, what action fires at end-of-life
|
||||
(delete, archive, anonymize), and what exception path exists (a
|
||||
legal hold suspends deletion).
|
||||
- Deletion at end-of-life is a feature, not a failure. A system that
|
||||
cannot delete on schedule is a system that over-retains, which is
|
||||
the symmetric violation of a system that under-retains. Both are
|
||||
P3 violations; the policy is the arbiter.
|
||||
- "We kept it because the bucket was cheap" is a violation. "We
|
||||
deleted it because the policy said to" is correct. Cost does not
|
||||
override policy; policy is the contract.
|
||||
|
||||
## Retention Policy as Code
|
||||
|
||||
- Retention rules live as code: lifecycle rules on the storage
|
||||
layer, scheduled deletion jobs, tiered storage transitions, and
|
||||
anonymization transforms. The code is versioned, reviewed, and
|
||||
auditable. A retention rule in a spreadsheet is a wishlist; the
|
||||
same rule in a reviewed, deployable lifecycle policy is a control.
|
||||
|
||||
```
|
||||
// illustrative lifecycle policy (abstracted, no vendor DSL)
|
||||
// object-storage lifecycle
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"name": "user-events-90d",
|
||||
"match": { "prefix": "events/" },
|
||||
"transitions": [
|
||||
{ "after": "30d", "to": "tier-cold" },
|
||||
{ "after": "90d", "action": "delete" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "audit-log-7y",
|
||||
"match": { "prefix": "audit/" },
|
||||
"transitions": [
|
||||
{ "after": "365d", "to": "tier-archive" },
|
||||
{ "after": "2555d", "action": "delete" }
|
||||
],
|
||||
"legal_hold": "suspends-action"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- The retention policy is itself auditable: which rule fired when,
|
||||
against which objects, with what result. The deletion events are
|
||||
logged (`audit-logs.md`) — deletion is a significant action.
|
||||
|
||||
## Retention vs. Backup — The Distinction
|
||||
|
||||
- A **backup** is a recovery mechanism: it exists to restore data
|
||||
after loss. A **retention rule** is a deletion mechanism: it
|
||||
exists to remove data at end-of-life. Conflating them produces
|
||||
data that survives both the deletion policy and the disaster —
|
||||
which is the opposite of compliance.
|
||||
- A backup is governed by a recovery-point / recovery-time objective;
|
||||
a retention rule is governed by a lifetime. They are independent
|
||||
contracts. A backup that is also the retention store is a store
|
||||
where nothing is ever deleted, which is a P3 violation.
|
||||
- A legal hold suspends retention deletion for a defined data set
|
||||
(e.g. data under investigation). The hold is itself a policy
|
||||
action, auditable and time-bounded, not a manual override.
|
||||
|
||||
## Retention is Distinct per Data Class
|
||||
|
||||
- Different data classes have different lifetimes. The retention
|
||||
policy enumerates the classes and their rules; it does not apply
|
||||
one number to everything. Typical classes (the names are
|
||||
abstract; the periods are policy decisions, not regulation-
|
||||
specific):
|
||||
- **Audit logs** — long, often multi-year, governed by
|
||||
accountability needs (`audit-logs.md`).
|
||||
- **User-generated content** — tied to the user's account
|
||||
lifetime; deletion follows account deletion (cross P8 Subject
|
||||
Access).
|
||||
- **Telemetry / metrics** — short, governed by observability need
|
||||
(`domains/observability/metrics.md`); high-resolution data ages
|
||||
to downsampled aggregates.
|
||||
- **Evidence artifacts** — tied to the audit cycle
|
||||
(`evidence.md`); the cycle ends, the evidence ages out.
|
||||
- A single retention rule for "all data" is a C3 (Simplicity)
|
||||
violation of the wrong kind: it is simpler than the requirement
|
||||
allows.
|
||||
|
||||
## Subject Access is Honored (P8 Subject Access is Honored)
|
||||
|
||||
- Data-subject rights — access (what do we have on this subject),
|
||||
export (in a portable form), deletion (and prove it), correction
|
||||
— are operations with defined contracts and audit trails, not
|
||||
ad-hoc tickets. The system implements them as first-class
|
||||
operations; a subject-access request that requires a forensics
|
||||
team is a correctness defect.
|
||||
- Retention and subject access interact at deletion: a subject
|
||||
deletion request fires the deletion policy for that subject's
|
||||
data, the deletion is audited, and the proof of deletion is
|
||||
returned to the subject (and recorded). A subject deletion that
|
||||
skips the audit is a P8 violation dressed as a P3 success.
|
||||
- Cross `domains/data/schema-design.md`: subject access is only
|
||||
computable if the schema tags which records belong to which
|
||||
subject. A schema with no subject linkage cannot honor a subject
|
||||
request — it cannot find the data to delete.
|
||||
|
||||
## Retention Migration Discipline
|
||||
|
||||
- Retention rules change. When the policy changes (a class's
|
||||
lifetime shortens or lengthens), the change is a migration: the
|
||||
new rule applies to data ingested after the cutover, and a
|
||||
backfill applies the new rule to existing data where applicable.
|
||||
Cross `domains/data/migrations.md` for the schema-lifecycle
|
||||
discipline this mirrors.
|
||||
- A retention rule change that is not versioned, not reviewed, and
|
||||
not backfilled is a P3 violation: the policy is not actually the
|
||||
policy if the storage layer does not reflect it.
|
||||
|
||||
## What Violates Retention Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Data kept indefinitely because "storage is cheap" | P3 Retention is Policy, Not Storage |
|
||||
| A retention rule in a spreadsheet, not in code | P3 Retention is Policy, Not Storage |
|
||||
| A backup bucket used as the retention store (nothing ever deletes) | P3 Retention is Policy, Not Storage |
|
||||
| A single retention period applied to all data classes | P3 Retention is Policy, Not Storage |
|
||||
| A subject deletion with no audit record of the deletion | P8 Subject Access is Honored |
|
||||
| A subject-access request that requires a forensics team | P8 Subject Access is Honored |
|
||||
| A schema with no subject linkage (cannot find data to delete) | P8 Subject Access is Honored |
|
||||
| A legal hold applied ad hoc, not as a policy action | P3 Retention is Policy, Not Storage |
|
||||
| A retention rule change with no backfill to existing data | P3 Retention is Policy, Not Storage |
|
||||
|
||||
## Relationship to Other Domains
|
||||
|
||||
- `domains/data/schema-design.md` — retention requires the schema
|
||||
to tag data class and subject linkage; subject access is only
|
||||
computable over a schema that supports it.
|
||||
- `domains/data/migrations.md` — retention rule changes are
|
||||
migrations; the discipline (version, review, backfill) mirrors
|
||||
schema migrations.
|
||||
- `domains/compliance/audit-logs.md` — audit logs have their own
|
||||
retention floor; deletion of an audit segment is meta-audited.
|
||||
- `domains/compliance/evidence.md` — evidence artifacts have a
|
||||
retention lifecycle tied to the audit cycle.
|
||||
- `domains/observability/metrics.md` — telemetry retention is
|
||||
governed by observability need; high-res data ages to aggregates.
|
||||
- `domains/security/secrets.md` — secrets have a retention lifecycle
|
||||
tied to rotation; a secret past its rotation date is overdue, not
|
||||
retained.
|
||||
@@ -0,0 +1,175 @@
|
||||
# Evidence — Derived Rules
|
||||
|
||||
> Derives from `domains/compliance/first-principles.md`. Covers P6
|
||||
> (Evidence is Collected Continuously), P5 (Policy is a Gate, so
|
||||
> decisions are evidence), P7 (Identity Attributable, so evidence
|
||||
> has provenance), and P10 (Posture Observable, so evidence is
|
||||
> queryable). Referenced by `audit-logs.md` (logs are evidence)
|
||||
> and `data-retention.md` (evidence has a lifecycle).
|
||||
|
||||
## Evidence is Collected Continuously (P6 Evidence is Collected Continuously)
|
||||
|
||||
- Evidence of compliance — logs, configs, scans, attestations,
|
||||
policy decisions, access reviews — is gathered as a byproduct of
|
||||
operation, not assembled manually at audit time. The audit-time
|
||||
scramble is the anti-pattern: it is expensive, it is incomplete,
|
||||
and it produces evidence that is reconstructed rather than
|
||||
recorded.
|
||||
- Continuous evidence collection means the audit packet is a query
|
||||
over already-collected artifacts, not a forensic reconstruction.
|
||||
The auditor asks "show me the access reviews for Q3" and the
|
||||
answer is a query against the evidence store, not a six-week
|
||||
project.
|
||||
- This is the compliance angle on `domains/observability/tracing.md`
|
||||
for distributed evidence (a trace spans the request that produced
|
||||
the evidence) and `domains/observability/metrics.md` for posture
|
||||
signals (a metric is a continuous evidence stream).
|
||||
|
||||
## Evidence is a Byproduct, Not a Deliverable
|
||||
|
||||
- Evidence collected as a byproduct is trustworthy: it records what
|
||||
happened, when it happened, recorded by the system that did it.
|
||||
Evidence assembled at audit time is less trustworthy: it records
|
||||
what someone remembered to write down, when they wrote it, after
|
||||
the fact.
|
||||
- The mechanism: every significant action (`audit-logs.md`) emits
|
||||
its record to an evidence store; every policy decision
|
||||
(`policy-as-code.md`) emits its decision; every deployment emits
|
||||
its signed attestation; every access review emits its result. The
|
||||
store is append-only (`audit-logs.md` P1), queryable (P10), and
|
||||
retention-bound (`data-retention.md`).
|
||||
|
||||
## Provenance and Identity (P7 Identity is Attributable)
|
||||
|
||||
- Evidence has provenance: which system produced it, when, from what
|
||||
input. An evidence artifact with no provenance is anecdote, not
|
||||
evidence — it cannot be attributed to a source, so it cannot be
|
||||
trusted.
|
||||
- Provenance includes the identity of the producer (a workload
|
||||
identity, a service account) and the chain of custody (who has
|
||||
had access to the artifact since it was produced). Cross
|
||||
`domains/security/authorization.md`: the producer's identity is
|
||||
authenticated, not assumed.
|
||||
|
||||
## Signed Attestations (IDEATE-29)
|
||||
|
||||
- A signed attestation is evidence with a cryptographic signature
|
||||
binding the artifact to its producer. The signature is the
|
||||
provenance: it can be verified independently of the producer, and
|
||||
it cannot be forged without the producer's key. Cross
|
||||
`domains/security/supply-chain.md` for the supply-chain angle.
|
||||
- Cosign (Sigstore) and in-toto are the canonical patterns: a
|
||||
builder signs an artifact (container image, deployable, evidence
|
||||
bundle) at production time; a verifier checks the signature at
|
||||
consumption time. The signature is the evidence that the artifact
|
||||
came from where it claims to have come from.
|
||||
|
||||
- **Illustrative signed attestation (Cosign / Sigstore format, NOT
|
||||
a real signature — illustrative only, no live keys):**
|
||||
|
||||
```
|
||||
// Cosign attest — bind an attestation to an image digest
|
||||
// (illustrative; not a real signature)
|
||||
$ cosign attest --type spdxjson \
|
||||
--predicate sbom.spdx.json \
|
||||
my-registry/app@sha256:5a3e1c...f9b2
|
||||
|
||||
// The attestation is stored as a signature in the registry,
|
||||
// bound to the image digest. The payload is a DSSE envelope:
|
||||
|
||||
{
|
||||
"payloadType": "application/vnd.in-toto+json",
|
||||
"payload": "eyJfdHlwZSI6ImF0dGVzdGF0aW9uIn0...",
|
||||
"signatures": [
|
||||
{
|
||||
"sig": "MEUCIQDx...illustrative-base64-signature...==",
|
||||
"keyid": "cosign-key-2024-q4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// The decoded payload (an in-toto statement binding the
|
||||
// attestation to the image digest):
|
||||
{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"predicateType": "https://spdx.dev/Document",
|
||||
"subject": [
|
||||
{
|
||||
"name": "my-registry/app",
|
||||
"digest": { "sha256": "5a3e1c...f9b2" }
|
||||
}
|
||||
],
|
||||
"predicate": {
|
||||
"SPDXID": "SPDXRef-DOCUMENT",
|
||||
"creationInfo": {
|
||||
"created": "2024-11-07T15:03:22Z",
|
||||
"creators": ["Tool: atelier-build-pipeline"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verification (independent of the producer):
|
||||
$ cosign verify-attestation --type spdxjson \
|
||||
--certificate-identity-regexp '.*atelier-build.*' \
|
||||
my-registry/app@sha256:5a3e1c...f9b2
|
||||
// Verification succeeded for: my-registry/app@sha256:5a3e1c...f9b2
|
||||
// SBOM attestation found for subject
|
||||
```
|
||||
|
||||
- The attestation is illustrative — the signatures and digests are
|
||||
not real. The shape (DSSE envelope, in-toto statement, subject +
|
||||
predicate, verify-by-identity) is what evidence-as-attestation
|
||||
looks like. A real attestation carries a real signature from a
|
||||
real key held by the builder.
|
||||
|
||||
## Audit-Ready Export
|
||||
|
||||
- The evidence store is queryable at any time, not only at audit
|
||||
time. The audit packet is a query (a date range, a data class, a
|
||||
subject) over the store; the export is a dump of the matching
|
||||
artifacts with their provenance and signatures.
|
||||
- An audit-ready export that requires six weeks of forensics is a
|
||||
P6 violation dressed as a success: the evidence was not collected
|
||||
continuously, it was reconstructed. The export should be a query
|
||||
that runs in minutes, not a project that runs for weeks.
|
||||
|
||||
## Evidence Lifecycle
|
||||
|
||||
- Evidence has a retention lifecycle (`data-retention.md`): an
|
||||
evidence artifact is retained for the audit cycle it supports,
|
||||
then ages out. The retention rule for evidence is itself audited
|
||||
(deletion of evidence is a meta-audited action, like deletion of
|
||||
audit logs).
|
||||
- A legal hold suspends evidence deletion for a defined set — the
|
||||
same mechanism as audit-log holds.
|
||||
|
||||
## What Violates Evidence Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Evidence assembled by hand the week before an audit | P6 Evidence is Collected Continuously |
|
||||
| An evidence artifact with no provenance (no producer, no timestamp) | P7 Identity is Attributable |
|
||||
| An audit packet that requires six weeks of forensics to produce | P6 Evidence is Collected Continuously |
|
||||
| An attestation with no signature (provenance asserted, not proven) | P7 Identity is Attributable |
|
||||
| Evidence store not queryable between audits | P10 Compliance Posture is Observable |
|
||||
| Evidence deleted before its retention period with no meta-audit | P6 Evidence is Collected Continuously |
|
||||
| Policy decisions not recorded as evidence | P5 Policy is Evaluated as a Gate |
|
||||
| A deployment with no signed attestation of its build provenance | P7 Identity is Attributable |
|
||||
|
||||
## Relationship to Other Domains
|
||||
|
||||
- `domains/security/supply-chain.md` — signed attestations are the
|
||||
supply-chain integrity primitive; evidence.md is the compliance
|
||||
consumer of the same artifact.
|
||||
- `domains/observability/metrics.md` — posture metrics are a
|
||||
continuous evidence stream.
|
||||
- `domains/observability/tracing.md` — distributed traces provide
|
||||
evidence that spans a request across services.
|
||||
- `domains/compliance/audit-logs.md` — audit logs are a primary
|
||||
evidence artifact; the append-only guarantee is what makes them
|
||||
admissible.
|
||||
- `domains/compliance/policy-as-code.md` — policy decisions are
|
||||
evidence of enforcement; the policy code itself is evidence of
|
||||
the rule.
|
||||
- `domains/compliance/data-retention.md` — evidence has a retention
|
||||
lifecycle tied to the audit cycle.
|
||||
@@ -0,0 +1,184 @@
|
||||
# Compliance — First Principles
|
||||
|
||||
> Framework-agnostic per D-024. These principles derive from core
|
||||
> Security (a subset of C1 Correctness), Observability, and
|
||||
> Reversibility. They apply across regulations — NIST CSF, SOC 2,
|
||||
> GDPR, CCPA, HIPAA, PCI-DSS, ISO 27001 — without prescribing any
|
||||
> regulation-specific implementation. Regulation names appear here
|
||||
> only as examples of what the principles support; the principles
|
||||
> themselves are engineering rules, not legal controls.
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Audit Logs are Append-Only
|
||||
Audit records are immutable once written. Deletion or mutation of an
|
||||
audit record is itself an auditable incident — the tampering is the
|
||||
signal, not just the underlying event. An audit log that can be edited
|
||||
is not an audit log; it is a draft. Append-only is enforced
|
||||
structurally (write-once storage, immutable buckets, hash-chained
|
||||
records), not by policy alone. This is the compliance angle on
|
||||
`domains/observability/logging.md`: structured logs that cannot be
|
||||
rewritten are the substrate of accountability.
|
||||
|
||||
### P2. Every Significant Action is Logged
|
||||
The set of auditable actions is defined a priori, in code, before the
|
||||
action ships — not retrofitted after an incident. Authentication
|
||||
changes, authorization decisions, data access, configuration changes,
|
||||
policy changes, and deletions are all significant. "We forgot to log
|
||||
it" is a violation, not an excuse. The auditable-action catalog is
|
||||
itself versioned and reviewed. A significant action with no log line
|
||||
is a C7 (Observability) defect and a C1 (Correctness) defect: the
|
||||
system's behavior is invisible, and accountability is impossible.
|
||||
|
||||
### P3. Retention is Policy, Not Storage
|
||||
Data lifetime is declared and enforced as policy, not left to the
|
||||
storage layer's defaults. Deletion at end-of-life is a feature, not a
|
||||
failure. Retention rules live as code (lifecycle rules, scheduled
|
||||
deletion jobs, tiered storage transitions), they are reviewed, and
|
||||
they are auditable. "We kept it because the bucket was cheap" is a
|
||||
violation; "we deleted it because the policy said to" is correct.
|
||||
Retention is distinct from backup: a backup is a recovery mechanism,
|
||||
a retention rule is a deletion mechanism. Keeping them conflated
|
||||
produces data that survives both the deletion policy and the
|
||||
disaster — which is the opposite of compliance. Cross
|
||||
`domains/data/migrations.md` for the schema-lifecycle discipline.
|
||||
|
||||
### P4. Policy is Code
|
||||
Compliance policy is expressed in versioned, reviewable, testable
|
||||
code (OPA / Rego, AWS Cedar, HashiCorp Sentinel, Kyverno) — not in
|
||||
spreadsheets, prose documents, or tribal knowledge. Policy in a
|
||||
spreadsheet is untestable, unreviewable, and undeployable; it is a
|
||||
wishlist, not a control. Policy-as-code inherits the disciplines of
|
||||
`domains/infrastructure-as-code/P1 Declarative Intent`: declarative
|
||||
intent, version control, review before merge, plan before apply. A
|
||||
compliance rule that is not executable is a rule that cannot be
|
||||
enforced, which is a rule that does not exist.
|
||||
|
||||
### P5. Policy is Evaluated as a Gate
|
||||
Policy violations block before the action, not after the audit.
|
||||
Enforcement happens at admission time (kubernetes admission), at
|
||||
pipeline time (CI/CD gates), and at provisioning time (IaC plan
|
||||
gates) — before the non-compliant state is realized. Detecting a
|
||||
violation after it ships is detection, not enforcement. A policy that
|
||||
is "logged but not blocked" is a postcard, not a gate. This is the
|
||||
compliance angle on C5 (Reversibility): a blocked action is
|
||||
reversible by construction; a shipped violation requires remediation,
|
||||
which is more expensive than prevention.
|
||||
|
||||
### P6. Evidence is Collected Continuously
|
||||
Evidence of compliance — logs, configs, scans, attestations, policy
|
||||
decisions, access reviews — is gathered as a byproduct of operation,
|
||||
not assembled manually at audit time. The audit-time scramble is the
|
||||
anti-pattern: it is expensive, it is incomplete, and it produces
|
||||
evidence that is reconstructed rather than recorded. Continuous
|
||||
evidence collection means the audit packet is a query over
|
||||
already-collected artifacts, not a forensic reconstruction. This is
|
||||
the compliance angle on `domains/observability/tracing.md` for
|
||||
distributed evidence and `domains/observability/metrics.md` for
|
||||
posture signals.
|
||||
|
||||
### P7. Identity is Attributable
|
||||
Every logged action traces to an authenticated, non-shared principal.
|
||||
Shared accounts, generic service identities, and "admin" as an actor
|
||||
are violations: an action with no attributable human or workload is
|
||||
an action with no accountability. Identity is recorded in the audit
|
||||
record at the time of the action, not resolved after the fact. This
|
||||
is the compliance angle on `domains/security/authorization.md` and
|
||||
`domains/kubernetes/rbac.md`: the audit subject must be the principal
|
||||
that acted, not a role that many can assume.
|
||||
|
||||
### P8. Subject Access is Honored
|
||||
Data-subject rights — access, export, deletion, correction — are
|
||||
operations with defined contracts and audit trails, not ad-hoc
|
||||
tickets. The system can answer "what do we have on this subject,"
|
||||
"export it in a portable form," and "delete it and prove the
|
||||
deletion" as first-class operations. These are not features bolted on
|
||||
at the end; they are contracts the data layer implements from the
|
||||
start. A subject-access request that requires a forensics team is a
|
||||
correctness defect: the system does not know what it holds. Cross
|
||||
`domains/data/schema-design.md` for the data shapes that make
|
||||
subject access computable.
|
||||
|
||||
### P9. Secrets and Sensitive Data are Redacted in Audit
|
||||
Audit logs themselves must not leak secrets, credentials, PII, or
|
||||
other sensitive data. Redaction is structural — applied at the
|
||||
logging boundary, before the record is written — not opportunistic
|
||||
scrubbing after the fact. A secret that appears in an audit log is a
|
||||
C1 (Correctness) violation (the log is now a secret store) and a
|
||||
security violation (`domains/security/secrets.md`). The redaction
|
||||
policy is itself auditable: which fields are redacted, by what rule,
|
||||
in which log stream. Once a secret is in an append-only log, the
|
||||
remediation is expensive — rotate the secret and rewrite the log's
|
||||
access scope — so redaction-at-source is the only sound position.
|
||||
|
||||
### P10. Compliance Posture is Observable
|
||||
The system reports its own compliance state: drift from policy, open
|
||||
violations, retention status, evidence freshness, policy-evaluation
|
||||
counts. Silent non-compliance is the bug. A compliance posture
|
||||
metric is a first-class signal (`domains/observability/metrics.md`),
|
||||
alertable, and dashboarded. "We didn't know we were non-compliant"
|
||||
is not a defense; it is a C7 (Observability) defect. The posture is
|
||||
queryable at any time, not only at audit time. This is the compliance
|
||||
angle on `domains/infrastructure-as-code/P3 State is Truth`: the
|
||||
compliance state is a versioned, queryable truth, not a vibe.
|
||||
|
||||
## 2. Core Principle Trace
|
||||
|
||||
Each compliance P-rule derives from one or more core C-rules (C1–C8).
|
||||
The matrix extension lands in P4 of the v0.3 plan; the traces below
|
||||
are authoritative.
|
||||
|
||||
| P-rule | Core | Why |
|
||||
|--------|------|-----|
|
||||
| P1 Audit Logs are Append-Only | C1, C5 | Correctness of the record; reversibility of tamper detection |
|
||||
| P2 Every Significant Action is Logged | C7, C1 | Observability of behavior; correctness of a-priori audit scope |
|
||||
| P3 Retention is Policy, Not Storage | C5, C8 | Reversibility of data lifetime; economy of storage as policy |
|
||||
| P4 Policy is Code | C6, C2 | Composability of versioned policy; clarity of executable intent |
|
||||
| P5 Policy is Evaluated as a Gate | C1, C5 | Correctness of pre-action enforcement; reversibility of blocked actions |
|
||||
| P6 Evidence is Collected Continuously | C7, C3 | Observability of compliance state; simplicity of audit-by-query |
|
||||
| P7 Identity is Attributable | C1, C7 | Correctness of accountability (security subset); observability of who acted |
|
||||
| P8 Subject Access is Honored | C1, C5 | Correctness of the data-subject contract; reversibility of deletion |
|
||||
| P9 Secrets and Sensitive Data are Redacted in Audit | C1, C3 | Correctness of not leaking (security subset); simplicity of structural redaction |
|
||||
| P10 Compliance Posture is Observable | C7, C1 | Observability of posture; correctness of self-reported state |
|
||||
|
||||
## 3. What Violates These Principles
|
||||
|
||||
| Violation | Principle Breached |
|
||||
|-----------|-------------------|
|
||||
| An audit log stored on a mutable filesystem with no write-once protection | P1 Audit Logs are Append-Only |
|
||||
| A `DELETE` on an audit record to "clean up a typo" | P1 Audit Logs are Append-Only |
|
||||
| An auth change with no audit log line | P2 Every Significant Action is Logged |
|
||||
| "We'll add logging after we ship the feature" | P2 Every Significant Action is Logged |
|
||||
| Data kept indefinitely because "the bucket is cheap" | P3 Retention is Policy, Not Storage |
|
||||
| A retention rule in a spreadsheet, not in code | P4 Policy is Code |
|
||||
| A policy that logs violations but does not block the action | P5 Policy is Evaluated as a Gate |
|
||||
| Evidence assembled by hand the week before an audit | P6 Evidence is Collected Continuously |
|
||||
| A shared `admin` account as the audit actor | P7 Identity is Attributable |
|
||||
| A subject-access request that requires a forensics team | P8 Subject Access is Honored |
|
||||
| A secret visible in an audit log entry | P9 Secrets and Sensitive Data are Redacted in Audit |
|
||||
| No dashboard for compliance posture between audits | P10 Compliance Posture is Observable |
|
||||
|
||||
## 4. Relationship to Other Domains
|
||||
|
||||
Compliance is the accountability layer that crosses
|
||||
`domains/security/` (it audits security actions),
|
||||
`domains/observability/` (audit logs are structured logging; posture
|
||||
is metrics; evidence is traces), `domains/data/` (retention and
|
||||
subject access are data-layer contracts), and
|
||||
`domains/infrastructure-as-code/` (policy-as-code parallels
|
||||
declarative IaC; compliance state parallels state-as-truth). Cross-
|
||||
links are one-directional (per D-026 extended):
|
||||
|
||||
- `domains/security/authorization.md` ← P7 (attributable identity)
|
||||
- `domains/security/secrets.md` ← P9 (redaction)
|
||||
- `domains/security/supply-chain.md` ← P6 (signed attestations as evidence)
|
||||
- `domains/observability/logging.md` ← P1, P2 (audit logs = structured logging)
|
||||
- `domains/observability/metrics.md` ← P10 (compliance posture metrics)
|
||||
- `domains/observability/tracing.md` ← P6 (evidence from distributed traces)
|
||||
- `domains/data/schema-design.md` ← P3, P8 (retention and subject-access shapes)
|
||||
- `domains/data/migrations.md` ← P3 (retention migration discipline)
|
||||
- `domains/infrastructure-as-code/P1 Declarative Intent` ← P4 (policy-as-code)
|
||||
- `domains/infrastructure-as-code/P3 State is Truth` ← P10 (compliance posture truth)
|
||||
- `domains/kubernetes/rbac.md` ← P7 (audit subject identity)
|
||||
- `domains/devops/ci-cd.md` ← P5 (policy as a pipeline gate)
|
||||
- `domains/devops/first-principles.md` ← P4 (policy as configuration-as-code)
|
||||
@@ -0,0 +1,140 @@
|
||||
# Policy as Code — Derived Rules
|
||||
|
||||
> Derives from `domains/compliance/first-principles.md`. Covers P4
|
||||
> (Policy is Code) and P5 (Policy is Evaluated as a Gate). Referenced
|
||||
> by `audit-logs.md` (policy decisions are audited) and `evidence.md`
|
||||
> (policy decisions are evidence). Framework-agnostic per D-024.
|
||||
|
||||
## Policy is Code (P4 Policy is Code)
|
||||
|
||||
- Compliance policy is expressed in versioned, reviewable, testable
|
||||
code — not in spreadsheets, prose documents, or tribal knowledge.
|
||||
Policy in a spreadsheet is untestable, unreviewable, and
|
||||
undeployable; it is a wishlist, not a control.
|
||||
- Policy-as-code inherits the disciplines of
|
||||
`domains/infrastructure-as-code/P1 Declarative Intent`: declarative
|
||||
intent, version control, review before merge, plan before apply.
|
||||
A compliance rule that is not executable is a rule that cannot be
|
||||
enforced, which is a rule that does not exist.
|
||||
- Policy code is tested like any other code: unit tests for the rule
|
||||
logic (given an input, the rule allows or denies as expected),
|
||||
integration tests for the gate (the rule fires at the right point
|
||||
in the pipeline), and versioning for the policy itself (a policy
|
||||
change is a reviewed, merged, deployed change).
|
||||
|
||||
## Policy is Evaluated as a Gate (P5 Policy is Evaluated as a Gate)
|
||||
|
||||
- Policy violations block **before** the action, not after the
|
||||
audit. Enforcement happens at:
|
||||
- **Admission time** — a kubernetes admission webhook denies a
|
||||
non-compliant resource before it is created
|
||||
(`domains/kubernetes/rbac.md`).
|
||||
- **Pipeline time** — a CI/CD gate denies a non-compliant change
|
||||
before it merges (`domains/devops/ci-cd.md`).
|
||||
- **Provisioning time** — an IaC plan gate denies a non-compliant
|
||||
resource before `apply` (`domains/infrastructure-as-code/`).
|
||||
- A policy that logs violations but does not block the action is a
|
||||
postcard, not a gate. Detection is not enforcement. A logged
|
||||
violation that the actor could ignore is a P5 violation — the
|
||||
policy exists, but the system is not compliant by construction.
|
||||
- The gate is the contract. The policy author writes the rule; the
|
||||
gate operator wires the rule into the enforcement point; the
|
||||
auditor verifies the gate fired. All three are auditable
|
||||
(`audit-logs.md`).
|
||||
|
||||
## Engine Comparison (IDEATE-23)
|
||||
|
||||
| Engine | Policy Language | Evaluation Gate | Ecosystem | Notes |
|
||||
|--------|-----------------|-----------------|-----------|-------|
|
||||
| **OPA / Rego** | Rego (declarative, set-based, Datalog-inspired) | CI/CD, k8s admission (Gatekeeper), HTTP API, IaC plan (Terraform Sentinel-style), service mesh | Broadest ecosystem; CNCF graduated; library of reusable bundles | General-purpose; the default choice when the gate location varies |
|
||||
| **AWS Cedar** | Cedar (declarative, authorization-focused, schema-typed) | k8s admission (via Cedar-agent), application authorization, AVP (Verified Permissions) | AWS-native; tight schema typing; separates policy from entities | Authorization-focused; strong where the policy is "who can do what on which resource" |
|
||||
| **HashiCorp Sentinel** | Sentinel (declarative, restricted, policy-focused) | Terraform / TFE plan gate, Nomad, Vault | HashiCorp ecosystem; embedded in Terraform Enterprise / HCP | IaC-plan-gate native; the enforcement point is the `plan` output |
|
||||
| **Kyverno** | Kyverno (YAML-declarative, k8s-native, no new DSL) | k8s admission (native), cluster-wide policy reports | Kubernetes-native; no separate language — policy is a CRD | k8s-cluster-gate native; the choice when the gate is admission and the team prefers YAML over a DSL |
|
||||
|
||||
- None is advocated over the others. The choice is (a) where the
|
||||
gate fires, (b) the team's tolerance for a new policy language,
|
||||
and (c) ecosystem fit. All four satisfy P4/P5 when wired
|
||||
correctly.
|
||||
- A gate is a gate regardless of engine: the rule is declarative,
|
||||
the evaluation is pre-action, and the decision is allow-or-deny.
|
||||
The engine difference is language and enforcement-point fit, not
|
||||
correctness.
|
||||
|
||||
## Policy Testing
|
||||
|
||||
- Policy code is unit-tested like any other code. A test asserts
|
||||
that a given input produces the expected decision (allow / deny /
|
||||
warn). The test is versioned with the policy; a policy change
|
||||
with no test change is a red flag.
|
||||
|
||||
```
|
||||
// illustrative Rego policy + test
|
||||
// policy: deny containers running as root
|
||||
package k8s.admission
|
||||
|
||||
deny[msg] {
|
||||
input.kind == "Pod"
|
||||
c := input.spec.containers[_]
|
||||
not c.securityContext.runAsNonRoot
|
||||
msg := sprintf("container %s must set runAsNonRoot", [c.name])
|
||||
}
|
||||
|
||||
// test (Rego unit test)
|
||||
package k8s.admission
|
||||
|
||||
test_deny_root_container {
|
||||
some msg in deny with input as {
|
||||
"kind": "Pod",
|
||||
"spec": { "containers": [ { "name": "app", "securityContext": {} } ] }
|
||||
}
|
||||
msg == "container app must set runAsNonRoot"
|
||||
}
|
||||
```
|
||||
|
||||
- Integration tests assert the gate fires: a non-compliant resource
|
||||
submitted to the admission endpoint is denied; a compliant one is
|
||||
allowed. The integration test runs against the real gate, not a
|
||||
mock, because the gate wiring is half the contract.
|
||||
|
||||
## Policy Versioning
|
||||
|
||||
- Policy is versioned in git. A policy change is a reviewed, merged,
|
||||
deployed change — the same discipline as application code. A
|
||||
policy that is edited in production without review is a P4
|
||||
violation: the policy is code, but it is being treated as config.
|
||||
- A policy change can break existing workloads (a new deny rule
|
||||
blocks a previously-allowed resource). The rollout is staged:
|
||||
warn-only mode first (log violations, do not block), then enforce
|
||||
mode after the violation count is zero. This is the policy
|
||||
analogue of `domains/devops/P5 Progressive Delivery`.
|
||||
|
||||
## What Violates Policy-as-Code Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| A compliance rule in a spreadsheet | P4 Policy is Code |
|
||||
| A policy that logs violations but does not block the action | P5 Policy is Evaluated as a Gate |
|
||||
| A policy edited in production without review | P4 Policy is Code |
|
||||
| A policy with no unit tests for the rule logic | P4 Policy is Code |
|
||||
| A gate wired with a mock instead of the real engine | P5 Policy is Evaluated as a Gate |
|
||||
| A new deny rule enforced without a warn-only rollout | P5 Policy is Evaluated as a Gate |
|
||||
| A policy in prose ("the team should not use root containers") | P4 Policy is Code |
|
||||
| A policy decision with no audit record | P5 Policy is Evaluated as a Gate |
|
||||
|
||||
## Relationship to Other Domains
|
||||
|
||||
- `domains/infrastructure-as-code/first-principles.md` — policy-as-
|
||||
code inherits declarative intent, versioning, and plan-before-
|
||||
apply from IaC.
|
||||
- `domains/kubernetes/rbac.md` — k8s admission is a primary
|
||||
enforcement gate; Kyverno and OPA Gatekeeper wire into it.
|
||||
- `domains/devops/ci-cd.md` — CI/CD is a pipeline-time enforcement
|
||||
gate; a policy step blocks a non-compliant change before merge.
|
||||
- `domains/compliance/audit-logs.md` — every policy decision (allow
|
||||
/ deny) is an audited significant action.
|
||||
- `domains/compliance/evidence.md` — policy decisions and the
|
||||
policy code itself are evidence of enforcement posture.
|
||||
- `domains/security/authorization.md` — Cedar's authorization-
|
||||
focused policy overlaps with authz; the split is that authz is
|
||||
the runtime decision, policy-as-code is the reviewed rule that
|
||||
drives it.
|
||||
@@ -0,0 +1,173 @@
|
||||
# Internationalization (i18n) — First Principles
|
||||
|
||||
> Grounded in Unicode ICU + CLDR, W3C i18n WG, BCP 47 / RFC 5646,
|
||||
> ICU MessageFormat / FormatJS / i18next / Mozilla Fluent, the
|
||||
> JavaScript `Intl` API, and WCAG 2.1 AA. The developer's language is
|
||||
> one locale among many, not the neutral form.
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Source Language is a Locale, Not the Default
|
||||
The developer's own language is one locale among many — it is not the
|
||||
"neutral" or "unlocalized" form of the product. Strings are extracted
|
||||
from day one, addressed by key, and routed through a locale resource
|
||||
layer even when only one locale is populated. Treating the source
|
||||
language as the default produces hidden concatenations, hardcoded
|
||||
grammar assumptions, and a translation debt that compounds until the
|
||||
first second locale arrives — at which point the fix is a rewrite, not
|
||||
a patch. The source locale is `en-US` (or whatever the team writes in);
|
||||
it is not `null`. This is the i18n angle on `domains/uiux/copywriting.md`:
|
||||
copy lives in resources, not in code.
|
||||
|
||||
### P2. Locale Identifiers are Standardized
|
||||
Use BCP 47 language tags (`en-US`, `ar-EG`, `zh-Hans-CN`, `pt-BR`).
|
||||
No ad-hoc locale codes, no two-letter-only hacks, no invented keys.
|
||||
The tag carries language, script (when needed), and region (when
|
||||
needed); it is the contract between the resource layer, the
|
||||
formatting layer, and the runtime. A locale identifier that is not
|
||||
BCP 47 is a key that cannot be resolved by any standard tool, which
|
||||
is a correctness violation. Cross `domains/data/schema-design.md`:
|
||||
locale identifiers are a data shape with a defined vocabulary.
|
||||
|
||||
### P3. Resources are External, Not Inline
|
||||
User-facing strings live in locale resource files (`.po`, JSON,
|
||||
Fluent `.ftl`, ICU Resource Bundle), never concatenated inline in
|
||||
code. Inline strings are invisible to the translation pipeline,
|
||||
unversionable as a unit, and untestable for completeness. String
|
||||
concatenation in code (`"Welcome, " + name + "!"`) is the cardinal
|
||||
violation: it bakes in source-language grammar and breaks for every
|
||||
locale with different word order. Resources are the boundary; code
|
||||
addresses strings by key, the resource layer resolves the key to the
|
||||
locale. This is the i18n angle on C4 Locality: strings and their
|
||||
locale-specific consequences live together in the resource, not
|
||||
scattered across code.
|
||||
|
||||
### P4. Plural and Gender are Parameterized
|
||||
Plural forms, gender, and select are expressed with ICU MessageFormat
|
||||
(or an equivalent parameterized formatter), never with `if (n == 1)`
|
||||
branching in code. Plural rules are locale-specific — English has
|
||||
one/other, Arabic has six categories, Russian has three — and a
|
||||
hand-rolled branch encodes exactly one locale's rules while pretending
|
||||
to be universal. The formatter is the contract; the resource carries
|
||||
the variants; the code passes the count and lets the formatter choose.
|
||||
A `if (n == 1)` plural is a C1 (Correctness) violation masquerading as
|
||||
a shortcut.
|
||||
|
||||
### P5. Formatting is Locale-Aware
|
||||
Dates, times, numbers, currencies, units, and relative time are
|
||||
formatted via ICU / CLDR / the JavaScript `Intl` API — never
|
||||
hand-rolled. A hand-rolled date formatter encodes one locale's
|
||||
conventions and silently produces wrong output for every other locale
|
||||
(mm/dd/yyyy vs dd/mm/yyyy is the canonical failure). CLDR is the
|
||||
source of truth for locale data; `Intl` is the runtime that exposes
|
||||
it. Formatting correctness is observable: a misformatted date is a
|
||||
wrong answer in the user's locale, even if it is "right" in the
|
||||
developer's. Cross `domains/api/error-responses.md` for localized
|
||||
error messages at API boundaries.
|
||||
|
||||
### P6. Text Direction is a Layout Primitive
|
||||
RTL and bidi are first-class layout concerns, not a CSS afterthought.
|
||||
Logical CSS properties (`margin-inline-start`, `padding-block-end`,
|
||||
`inset-inline-end`) over physical (`margin-left`, `padding-top`). The
|
||||
`dir` attribute is set on the document and on subtrees; the bidi
|
||||
algorithm (UAX #9) handles inline reordering. A layout that assumes
|
||||
LTR is a layout that is wrong for `ar`, `he`, `fa`, `ur`, and any
|
||||
RTL-mixed context. Text direction is not a skin — it is a structural
|
||||
property of the layout, and fixing it late is a rewrite. This is the
|
||||
i18n angle on `domains/uiux/accessibility.md`: RTL support is an
|
||||
accessibility concern for non-Latin-script users.
|
||||
|
||||
### P7. Layout Accommodates Expansion
|
||||
Translated text expands and contracts — German is ~30% longer than
|
||||
English, Japanese often shorter, RTL mirroring shifts every visual
|
||||
anchor. Layouts are flexible: no fixed pixel widths for text, no
|
||||
truncation without an ellipsis-and-title strategy, no
|
||||
`white-space: nowrap` on translatable strings. A layout that breaks
|
||||
on a 30% expansion is a layout that is wrong for most of the world's
|
||||
locales. Designing for the worst-case expansion up front is cheaper
|
||||
than reworking every screen when the first long-form locale ships.
|
||||
|
||||
### P8. Pseudo-Locales Test Early
|
||||
Test with pseudo-locales (accented, lengthened, RTL-mirrored, brack-
|
||||
enclosed) before real translations arrive. A pseudo-locale run
|
||||
surfaces hardcoded strings, layout overflow, broken concatenation,
|
||||
and LTR assumptions while the fix is still cheap — the translator
|
||||
hasn't been paid yet, and the string freeze hasn't happened. Finding
|
||||
these bugs after real translation is a C5 (Reversibility) violation:
|
||||
the cost of undoing is now a re-translation. Cross
|
||||
`domains/testing/fixtures.md` and `domains/testing/pyramid.md` for
|
||||
where pseudo-locales sit in the testing pyramid.
|
||||
|
||||
### P9. Images and Icons are Cultural
|
||||
Icons, colors, gestures, and imagery are locale-sensitive. A
|
||||
mailbox icon means "email" in the US and "mail" in Japan — but a
|
||||
green checkmark means "correct" in the West and "incorrect" in some
|
||||
East Asian contexts. A thumbs-up is positive in much of the world
|
||||
and an insult in parts of the Middle East. Avoid locale-bound symbols
|
||||
as universal; parameterize imagery per locale where the symbol is not
|
||||
globally neutral. Icons are not a universal language; they are a
|
||||
locale with a picture. This is a C2 (Clarity) concern: an icon whose
|
||||
meaning changes by locale is unclear to the reader it was not drawn
|
||||
for.
|
||||
|
||||
### P10. Translation is Reversible and Versioned
|
||||
Resource files are versioned alongside code; a bad translation is a
|
||||
rollback, not a hot-patch. Every locale resource has a history
|
||||
(what shipped when), a provenance (which translator / which service),
|
||||
and a rollback path. A translation that breaks the UI is reverted to
|
||||
the prior resource version, the same way a code regression is
|
||||
reverted to the prior commit. Translations without version history
|
||||
are anecdote, not artifact — you cannot tell what changed, when, or
|
||||
why. This is the i18n angle on C5 Reversibility applied to the
|
||||
resource layer.
|
||||
|
||||
## 2. Core Principle Trace
|
||||
|
||||
Each i18n P-rule derives from one or more core C-rules (C1–C8). The
|
||||
matrix extension lands in P4 of the v0.3 plan; the traces below are
|
||||
authoritative.
|
||||
|
||||
| P-rule | Core | Why |
|
||||
|--------|------|-----|
|
||||
| P1 Source Language is a Locale, Not the Default | C2, C1 | Clarity of locale intent; correctness of treating source as one-of-many |
|
||||
| P2 Locale Identifiers are Standardized | C2, C6 | Clarity of a standard vocabulary; composability with standard tools |
|
||||
| P3 Resources are External, Not Inline | C4, C6 | Locality of strings and their locale consequences; composability of the resource layer |
|
||||
| P4 Plural and Gender are Parameterized | C1, C6 | Correctness of locale-specific plural rules; composability of the formatter contract |
|
||||
| P5 Formatting is Locale-Aware | C1, C7 | Correctness of formatted output; observability of format correctness |
|
||||
| P6 Text Direction is a Layout Primitive | C1, C4 | Correctness of layout for RTL; locality of direction with the text it governs |
|
||||
| P7 Layout Accommodates Expansion | C8, C3 | Economy of rework; simplicity of flexible layouts over per-locale overrides |
|
||||
| P8 Pseudo-Locales Test Early | C7, C5 | Observability of i18n defects early; reversibility of fixing before translation |
|
||||
| P9 Images and Icons are Cultural | C1, C2 | Correctness of locale-appropriate symbols; clarity of meaning across locales |
|
||||
| P10 Translation is Reversible and Versioned | C5 | Reversibility of the resource layer |
|
||||
|
||||
## 3. What Violates These Principles
|
||||
|
||||
| Violation | Principle Breached |
|
||||
|-----------|-------------------|
|
||||
| A user-facing string hardcoded in source | P3 Resources are External, Not Inline |
|
||||
| `"Welcome, " + name + "!"` string concatenation | P3 Resources are External, Not Inline |
|
||||
| `if (n == 1) { return "item"; } else { return "items"; }` | P4 Plural and Gender are Parameterized |
|
||||
| A locale code like `en_us` or `english` instead of `en-US` | P2 Locale Identifiers are Standardized |
|
||||
| A hand-rolled date formatter (`getMonth() + 1 + "/" + getDay()`) | P5 Formatting is Locale-Aware |
|
||||
| `margin-left: 10px` on a translatable layout | P6 Text Direction is a Layout Primitive |
|
||||
| A fixed-width text container that overflows on German | P7 Layout Accommodates Expansion |
|
||||
| First i18n test runs against real translations, not pseudo-locales | P8 Pseudo-Locales Test Early |
|
||||
| A thumbs-up icon shipped as universally positive | P9 Images and Icons are Cultural |
|
||||
| Resource files with no git history or no rollback path | P10 Translation is Reversible and Versioned |
|
||||
| The source language treated as the "unlocalized" default | P1 Source Language is a Locale, Not the Default |
|
||||
|
||||
## 4. Relationship to Other Domains
|
||||
|
||||
i18n is the locale-awareness layer that `domains/uiux/` consumes and
|
||||
that `domains/api/` surfaces at boundaries. It borrows the testing
|
||||
discipline of `domains/testing/` and the data-shape discipline of
|
||||
`domains/data/`. Cross-links are one-directional (per D-026 extended):
|
||||
|
||||
- `domains/uiux/copywriting.md` ← P1, P3 (strings live in resources)
|
||||
- `domains/uiux/accessibility.md` ← P6 (RTL is an a11y concern for non-Latin users)
|
||||
- `domains/uiux/components.md` ← P6, P7 (layout primitives that survive direction and expansion)
|
||||
- `domains/api/error-responses.md` ← P5 (localized error messages)
|
||||
- `domains/data/schema-design.md` ← P2, P3 (locale data shapes)
|
||||
- `domains/testing/fixtures.md` ← P8 (pseudo-locale fixtures)
|
||||
- `domains/testing/pyramid.md` ← P8 (pseudo-locale tier mapping)
|
||||
- `domains/testing/first-principles.md` ← P8 (testing discipline for locale)
|
||||
@@ -0,0 +1,141 @@
|
||||
# Formatting — Derived Rules
|
||||
|
||||
> Derives from `domains/i18n/first-principles.md`. Covers P2 (Locale
|
||||
> Identifiers Standardized), P4 (Plural/Gender Parameterized), and P5
|
||||
> (Formatting is Locale-Aware). Referenced by `locale-resources.md`
|
||||
> (the formatter resolves the messages) and `testing-i18n.md` (the
|
||||
> formatted output is what snapshots assert).
|
||||
|
||||
## Formatting is Locale-Aware (P5 Formatting is Locale-Aware)
|
||||
|
||||
- Dates, times, numbers, currencies, units, and relative time are
|
||||
formatted via ICU / CLDR / the JavaScript `Intl` API — never
|
||||
hand-rolled. CLDR is the source of truth for locale data; `Intl`
|
||||
is the runtime that exposes it.
|
||||
- A hand-rolled formatter encodes one locale's conventions and
|
||||
silently produces wrong output for every other locale. The
|
||||
canonical failure is date format: `mm/dd/yyyy` (US) vs
|
||||
`dd/mm/yyyy` (most of the world) vs `yyyy-mm-dd` (ISO, sortable).
|
||||
Picking one and calling it done is a correctness violation in
|
||||
every locale it is wrong for.
|
||||
|
||||
## BCP 47 Tags Drive Formatting (P2 Locale Identifiers Standardized)
|
||||
|
||||
- Every formatter takes a BCP 47 locale tag. The tag is the contract
|
||||
between the resource layer and the formatting layer: the same tag
|
||||
that selects the resource selects the formatter.
|
||||
- A locale tag that is not BCP 47 cannot be resolved by `Intl`, ICU,
|
||||
or CLDR — the formatter returns the runtime default, which is the
|
||||
developer's locale, not the user's. This is why P2 is a
|
||||
prerequisite of P5: you cannot format for a locale you cannot name.
|
||||
|
||||
## The Intl Surface (ICU/CLDR in the Browser and Node)
|
||||
|
||||
| API | Formats | Notes |
|
||||
|-----|---------|-------|
|
||||
| `Intl.DateTimeFormat` | Dates, times, date+time, time zones | Calendar (`buddhist`, `hebrew`, `islamic`), numbering system (`arab`, `hanidec`) via locale tag extensions |
|
||||
| `Intl.NumberFormat` | Numbers, currencies, units, percent | Notation (`compact`, `scientific`), grouping, sign display |
|
||||
| `Intl.RelativeTimeFormat` | "3 days ago", "in 2 months" | Locale-specific phrasing; numeric vs auto |
|
||||
| `Intl.PluralRules` | Plural category for a count | `one`, `few`, `many`, `other`, `zero`, `two` per CLDR — the engine ICU MessageFormat uses |
|
||||
| `Intl.ListFormat` | "a, b, and c" | Conjunction / disjunction / unit lists, locale-specific separators |
|
||||
| `Intl.Collator` | Locale-aware string sorting | Strength (`base`, `accent`, `case`); numeric collation |
|
||||
|
||||
- All of these are built on ICU/CLDR; they are the runtime baseline.
|
||||
Use them. A `moment.js`-style hand-rolled format string
|
||||
(`"MM/DD/YYYY"`) is a relic of the pre-`Intl` era and a P5
|
||||
violation in any locale-aware code path.
|
||||
|
||||
## Dates and Times
|
||||
|
||||
```
|
||||
// Correct — Intl, locale-aware
|
||||
new Intl.DateTimeFormat("ar-EG", {
|
||||
dateStyle: "full",
|
||||
timeStyle: "short",
|
||||
}).format(new Date());
|
||||
// "الأربعاء، ٧ نوفمبر ٢٠٢٤، ٣:١٥ م"
|
||||
|
||||
// Wrong — hand-rolled, source-locale only
|
||||
const d = new Date();
|
||||
const s = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();
|
||||
// "11/7/2024" — meaningless in most locales
|
||||
```
|
||||
|
||||
- Time zones are not locales. A locale tells you *how to format* a
|
||||
timestamp; a time zone tells you *what instant* it refers to. Do
|
||||
not derive one from the other (`ar-EG` is not a time zone).
|
||||
Format with the user's locale; render in the user's time zone;
|
||||
store in UTC.
|
||||
|
||||
## Numbers, Currencies, Units
|
||||
|
||||
```
|
||||
new Intl.NumberFormat("de-DE", { style: "currency", currency: "EUR" })
|
||||
.format(1234.56); // "1.234,56 €"
|
||||
|
||||
new Intl.NumberFormat("ar-EG", { style: "currency", currency: "EGP" })
|
||||
.format(1234.56); // "١٬٢٣٤٫٥٦ ج.م."
|
||||
|
||||
new Intl.NumberFormat("en-US", { style: "unit", unit: "kilometer-per-hour" })
|
||||
.format(100); // "100 km/h"
|
||||
```
|
||||
|
||||
- The currency code (`EUR`, `EGP`, `USD`) is ISO 4217; the locale
|
||||
determines the symbol, grouping, and placement. A hand-rolled
|
||||
`"$" + amount` is wrong for `de-DE` (symbol, grouping, placement
|
||||
all differ).
|
||||
|
||||
## Plural Rules (P4 Plural/Gender Parameterized)
|
||||
|
||||
- `Intl.PluralRules` returns the CLDR plural category for a count in
|
||||
a given locale. ICU MessageFormat uses this category to select the
|
||||
variant from the resource (`locale-resources.md`).
|
||||
- Never branch on the raw count in code. The count goes to the
|
||||
formatter; the formatter consults `PluralRules` for the locale;
|
||||
the resource carries the variant for that category.
|
||||
|
||||
```
|
||||
// ICU MessageFormat (FormatJS)
|
||||
new Intl.MessageFormat(
|
||||
"{count, plural, one {# item} other {# items}}",
|
||||
"en-US"
|
||||
).format({ count: 1 }); // "1 item"
|
||||
|
||||
// ar-EG — six categories; the code is identical, only the
|
||||
// resource differs.
|
||||
```
|
||||
|
||||
## Gender and Select
|
||||
|
||||
- ICU MessageFormat also supports `{gender, select, male {...} female {...} other {...}}`
|
||||
for gendered agreement and `{case, select, ...}` for general
|
||||
disjunction. These live in the resource, not in code branches.
|
||||
- A `switch (gender)` in code that picks a string is the same
|
||||
violation as `if (n == 1)`: it encodes one locale's grammar in
|
||||
code and breaks for every locale with different agreement rules.
|
||||
|
||||
## What Violates Formatting Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `getMonth() + 1 + "/" + getDay()` hand-rolled date | P5 Formatting is Locale-Aware |
|
||||
| `"$" + amount` hand-rolled currency | P5 Formatting is Locale-Aware |
|
||||
| `if (n === 1) "item" else "items"` plural branch | P4 Plural and Gender are Parameterized |
|
||||
| `moment("MM/DD/YYYY")` format string in locale-aware code | P5 Formatting is Locale-Aware |
|
||||
| Deriving time zone from locale tag | P2 Locale Identifiers are Standardized |
|
||||
| A non-BCP-47 tag passed to `Intl` (silently falls back) | P2 Locale Identifiers are Standardized |
|
||||
| `switch (gender)` selecting strings in code | P4 Plural and Gender are Parameterized |
|
||||
| Storing timestamps in local time, not UTC | P5 Formatting is Locale-Aware |
|
||||
|
||||
## Relationship to Other Domains
|
||||
|
||||
- `domains/api/error-responses.md` — API error messages are
|
||||
formatted for the requesting locale; the error code is stable, the
|
||||
message is locale-formatted.
|
||||
- `domains/data/schema-design.md` — locale identifiers, currency
|
||||
codes, and time zones are data contracts; treat them as schema
|
||||
(`en-US`, `EUR`, `UTC`), not free text.
|
||||
- `domains/i18n/locale-resources.md` — the resource layer carries
|
||||
the parameterized messages this formatter resolves.
|
||||
- `domains/testing/fixtures.md` — formatted output per locale is the
|
||||
fixture; snapshot tests assert against it.
|
||||
@@ -0,0 +1,138 @@
|
||||
# Locale Resources — Derived Rules
|
||||
|
||||
> Derives from `domains/i18n/first-principles.md`. Covers P1 (Source
|
||||
> Language is a Locale), P2 (Locale Identifiers Standardized), P3
|
||||
> (Resources External, Not Inline), P4 (Plural/Gender Parameterized),
|
||||
> and P10 (Translation Reversible and Versioned). Referenced by
|
||||
> `formatting.md` (strings the formatter resolves) and
|
||||
> `rtl-bidi.md` (the `dir` the resource layer carries).
|
||||
|
||||
## Resources are the Boundary (P3 Resources are External, Not Inline)
|
||||
|
||||
- User-facing strings live in locale resource files, addressed by
|
||||
key. Code references a key; the resource layer resolves the key to
|
||||
the active locale. The source language is itself a locale
|
||||
(`en-US`), not a fallback baked into code.
|
||||
- String concatenation in code (`"Welcome, " + name + "!"`) is the
|
||||
cardinal violation: it bakes in source-language word order and
|
||||
breaks for every locale with different grammar. Replace every
|
||||
concatenation with a parameterized message:
|
||||
`t("welcome", { name })`.
|
||||
- The resource is the single place a string lives. Editing a string
|
||||
in code instead of the resource is a locality violation (C4): the
|
||||
string and its locale consequences now live apart.
|
||||
|
||||
## Resource File Formats
|
||||
|
||||
| Format | Shape | When | Notes |
|
||||
|--------|-------|------|-------|
|
||||
| `.po` / `.pot` | gettext; msgid → msgstr, plural headers | Server-side, GNU ecosystem, PHP/Python/C | Mature tooling (`xgettext`, `msgmerge`); supports plural categories via header |
|
||||
| JSON (flat or namespaced) | `{ "key": "value" }` per locale | JS/web, i18next, FormatJS | Simple, machine-readable, but no native plural support — wrap with ICU MessageFormat |
|
||||
| Fluent `.ftl` | Mozilla FTL; asymmetric, resolver-driven | Browser-grade l10n, asymmetric translations | One message can resolve differently per locale without code changes; supports attributes, selectors |
|
||||
| ICU Resource Bundle | ICU binary/text resources | ICU-native, JVM, C++ | Tightest integration with ICU formatting/CLDR; steeper tooling |
|
||||
|
||||
- None is advocated over the others. The choice is ecosystem fit,
|
||||
not correctness. All four satisfy P3/P4 when used as the boundary.
|
||||
- A custom format (a hand-rolled `.csv` of strings) is a violation:
|
||||
it is unsupported by standard tooling, has no plural grammar, and
|
||||
cannot compose with `formatting.md`'s ICU layer.
|
||||
|
||||
## Key Naming and Namespaces (P2 Locale Identifiers Standardized)
|
||||
|
||||
- Locale identifiers are BCP 47 tags (`en-US`, `ar-EG`, `zh-Hans-CN`).
|
||||
No ad-hoc codes. The resource file is named for its locale:
|
||||
`en-US.json`, `ar-EG.po`, `ftl/ar-EG/main.ftl`.
|
||||
- Message keys are stable, semantic, and structured — not prose.
|
||||
`checkout.cart.item_count` not `"You have 3 items in your cart"`.
|
||||
A key that is the source string (`t("You have items")`) breaks the
|
||||
moment the source copy is edited; the key must outlive the copy.
|
||||
- Namespaces segment by surface (`checkout.*`, `errors.*`, `onboarding.*`)
|
||||
so that a locale can be loaded incrementally and so that key
|
||||
collisions across surfaces are impossible. A flat namespace with
|
||||
thousands of keys is a C2 (Clarity) violation waiting to happen.
|
||||
|
||||
## Fallback Chains
|
||||
|
||||
- The fallback chain is explicit: requested locale → language-only
|
||||
(`en` from `en-GB`) → default locale → key itself (last resort).
|
||||
The default locale is declared once, not re-derived in every call
|
||||
site.
|
||||
- A missing key in the requested locale falling back silently to the
|
||||
source locale is a P3 violation: the user is silently shown the
|
||||
developer's locale, which is not the locale they asked for. Missing
|
||||
keys must be observable (see `testing-i18n.md`).
|
||||
- Fallback is a property of the resource layer, not of individual
|
||||
components. A component that re-implements fallback is duplicating
|
||||
a contract (C6 Composability violation).
|
||||
|
||||
## Plural and Gender in Resources (P4 Plural/Gender Parameterized)
|
||||
|
||||
- Plural variants live in the resource, selected by the formatter,
|
||||
parameterized by the count. The code passes the count; the resource
|
||||
carries the variants; the formatter picks the right one per the
|
||||
locale's CLDR plural rules.
|
||||
|
||||
```
|
||||
// JSON + ICU MessageFormat (FormatJS / i18next)
|
||||
{
|
||||
"cart.item_count": "{count, plural, one {# item} other {# items}}"
|
||||
}
|
||||
// ar-EG.json — six plural categories per CLDR
|
||||
{
|
||||
"cart.item_count": "{count, plural, zero {لا عناصر} one {عنصر واحد} two {عنصران} few {# عناصر} many {# عنصرًا} other {# عنصر}}"
|
||||
}
|
||||
```
|
||||
|
||||
- `if (n == 1)` branching in code is a violation regardless of
|
||||
language. Arabic has six plural categories; Russian has three;
|
||||
English has two. A two-branch `if` encodes exactly one locale's
|
||||
rules and is wrong for every other.
|
||||
|
||||
## Extraction Tooling (P1, P3)
|
||||
|
||||
- Strings are extracted mechanically (e.g. `xgettext`, `i18next-
|
||||
parser`, FormatJS babel plugin), not by hand-tagging. Mechanical
|
||||
extraction produces a `.pot` template that translators work from;
|
||||
the template is regenerated on every build.
|
||||
- A string that cannot be extracted (built at runtime from
|
||||
fragments) is a P3 violation: it is invisible to the pipeline. If
|
||||
the extractor cannot see it, neither can the translator.
|
||||
- The extracted template is versioned (`P10`): the diff between
|
||||
templates is the change in translatable surface. A template that
|
||||
is not committed is a contract that is not reviewable.
|
||||
|
||||
## Versioning and Rollback (P10 Translation Reversible and Versioned)
|
||||
|
||||
- Resource files are committed to git alongside code. A bad
|
||||
translation is a `git revert` of the resource, not a hot-patch over
|
||||
the translator's work. Every locale resource has history,
|
||||
provenance (which translator / which service produced which
|
||||
commit), and a rollback path.
|
||||
- A locale resource that is generated by a translation service and
|
||||
committed without review is a P10 violation: the resource is
|
||||
versioned but the provenance is opaque. Review the diff the same
|
||||
way you review a code diff.
|
||||
|
||||
## What Violates Locale-Resource Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `t("You have " + n + " items")` concatenation | P3 Resources are External, Not Inline |
|
||||
| A custom `.csv` string store instead of a standard format | P3 Resources are External, Not Inline |
|
||||
| Locale file named `english.json` not `en-US.json` | P2 Locale Identifiers are Standardized |
|
||||
| `if (n == 1) { t("item") } else { t("items") }` in code | P4 Plural and Gender are Parameterized |
|
||||
| A key equal to the source string (`t("Welcome back")`) | P2 / P10 — keys must outlive copy |
|
||||
| Silent fallback to the source locale with no signal | P3 Resources are External, Not Inline |
|
||||
| A runtime-built string the extractor cannot see | P3 Resources are External, Not Inline |
|
||||
| Resource files committed by a bot with no human review | P10 Translation is Reversible and Versioned |
|
||||
|
||||
## Relationship to Other Domains
|
||||
|
||||
- `domains/uiux/copywriting.md` — copy lives in resources; UI
|
||||
microcopy is the source content the resource layer carries.
|
||||
- `domains/api/error-responses.md` — API error messages are locale-
|
||||
resource keys resolved at the boundary, not inline strings.
|
||||
- `domains/data/schema-design.md` — locale identifiers and resource
|
||||
shapes are a data contract; treat them as schema.
|
||||
- `domains/i18n/formatting.md` — the formatter resolves the
|
||||
parameterized message this layer produces.
|
||||
@@ -0,0 +1,117 @@
|
||||
# RTL and Bidi — Derived Rules
|
||||
|
||||
> Derives from `domains/i18n/first-principles.md`. Covers P6 (Text
|
||||
> Direction is a Layout Primitive) and P7 (Layout Accommodates
|
||||
> Expansion). Referenced by `testing-i18n.md` (RTL coverage is an
|
||||
> e2e tier). Grounded in W3C i18n bidi authoring, UAX #9, and
|
||||
> `domains/uiux/accessibility.md`.
|
||||
|
||||
## Text Direction is a Layout Primitive (P6 Text Direction is a Layout Primitive)
|
||||
|
||||
- RTL and bidi are first-class layout concerns, not a CSS
|
||||
afterthought. The layout is designed for both directions from the
|
||||
first commit, not retrofitted when an RTL locale ships.
|
||||
- Logical CSS properties over physical properties, always. The
|
||||
browser resolves logical → physical from the `dir` attribute; the
|
||||
code never has to.
|
||||
|
||||
| Physical (LTR-only) | Logical (dir-aware) | Resolves to in RTL |
|
||||
|---------------------|---------------------|--------------------|
|
||||
| `margin-left` | `margin-inline-start` | `margin-right` |
|
||||
| `margin-right` | `margin-inline-end` | `margin-left` |
|
||||
| `padding-left` | `padding-inline-start` | `padding-right` |
|
||||
| `left: 0` | `inset-inline-start: 0` | `right: 0` |
|
||||
| `text-align: left` | `text-align: start` | `text-align: right` |
|
||||
| `float: left` | use flexbox/grid + `inline-start` where supported | mirrored |
|
||||
|
||||
- The `dir` attribute is set on the document root (`<html dir="rtl">`)
|
||||
and on subtrees whose direction differs from the document
|
||||
(`<span dir="ltr">` for an embedded Latin run). `dir` is the
|
||||
contract the bidi algorithm (UAX #9) reads; do not fake direction
|
||||
with `text-align` alone.
|
||||
|
||||
## The Bidi Algorithm (UAX #9)
|
||||
|
||||
- The Unicode bidi algorithm resolves inline reordering of mixed-
|
||||
direction runs. The browser applies it; the author's job is to
|
||||
mark direction correctly, not to reorder by hand.
|
||||
- A string like `"The price is 15 USD"` in an RTL context renders
|
||||
with the Latin run `"15 USD"` in LTR within the RTL line — the
|
||||
algorithm handles it *if* the container's `dir` is set. Without
|
||||
`dir`, numbers and Latin fragments drift to the wrong edge.
|
||||
- `dir="auto"` on a container infers direction from the first strong
|
||||
directional character of its content — useful for user-generated
|
||||
content whose direction is unknown. `dir="auto"` is not a
|
||||
replacement for `dir="rtl"` on a known-RTL document.
|
||||
|
||||
## Mirroring (Icons, Controls, Diagrams)
|
||||
|
||||
- Direction-aware icons mirror in RTL: a "back" arrow pointing left
|
||||
in LTR points right in RTL. A "refresh" circular arrow does not
|
||||
mirror. The rule: icons that imply direction mirror; icons that
|
||||
imply time or rotation do not.
|
||||
- Use `[dir="rtl"]` selectors or logical icon variants — never
|
||||
`transform: scaleX(-1)` as a one-off hack scattered across
|
||||
components. Centralize the mirroring rule (a token, a component
|
||||
prop) so it is auditable.
|
||||
- Numbers do not mirror. `"15 USD"` in an RTL line is still
|
||||
`"15 USD"` left-to-right inside the bidi run; mirroring it to
|
||||
`"DSU 51"` is a correctness violation.
|
||||
- Diagrams and flowcharts: a left-to-right process flow in LTR is a
|
||||
right-to-left flow in RTL. Decide per diagram whether the flow
|
||||
mirrors (most do) or is direction-neutral (some scientific
|
||||
schematics).
|
||||
|
||||
## Layout Accommodates Expansion (P7 Layout Accommodates Expansion)
|
||||
|
||||
- Translated text expands. German is ~30% longer than English;
|
||||
Japanese is often shorter but taller; RTL mirroring shifts every
|
||||
visual anchor. Layouts are flexible:
|
||||
- No fixed pixel widths on translatable text containers.
|
||||
- No `white-space: nowrap` on translatable strings.
|
||||
- No `text-overflow: ellipsis` without a `title` carrying the full
|
||||
string.
|
||||
- Buttons sized to fit their longest locale variant, not the
|
||||
source.
|
||||
- A layout that breaks at +30% width is a layout that is wrong for
|
||||
most of the world's locales. Designing for the worst case up front
|
||||
is cheaper than reworking every screen when the first long-form
|
||||
locale ships.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
| Pitfall | Why it breaks | Fix |
|
||||
|---------|---------------|-----|
|
||||
| `margin-left` everywhere | In RTL the start is the right; `margin-left` leaves the right side unstyled | `margin-inline-start` |
|
||||
| `text-align: left` for "default" alignment | In RTL the default is right; `left` pins content to the wrong edge | `text-align: start` |
|
||||
| Icons hardcoded to LTR orientation | "Back" arrow points the wrong way in RTL | Mirror direction-implying icons via `[dir="rtl"]` |
|
||||
| Numbers mirrored with the layout | Numbers are LTR inside RTL; mirroring produces garbage | Leave number runs LTR; the bidi algorithm handles embedding |
|
||||
| Fixed `width: 120px` on a button | German button label overflows and truncates | `min-width` + `max-width` + flex; let content size |
|
||||
| `position: absolute; left: 0` | Pins to the physical left in both directions | `inset-inline-start: 0` |
|
||||
| Fake direction with `text-align` only | The bidi algorithm reads `dir`, not `text-align`; mixed runs reorder wrong | Set `dir` on the container |
|
||||
|
||||
## What Violates RTL/Bidi Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| A layout with no `dir` attribute, assuming LTR | P6 Text Direction is a Layout Primitive |
|
||||
| `margin-left` / `left: 0` / `text-align: left` throughout | P6 Text Direction is a Layout Primitive |
|
||||
| A "back" arrow that points left in the RTL build | P6 Text Direction is a Layout Primitive |
|
||||
| Numbers mirrored to read right-to-left | P6 Text Direction is a Layout Primitive |
|
||||
| `width: 100px` on a text container that overflows in German | P7 Layout Accommodates Expansion |
|
||||
| `white-space: nowrap` on a translated label | P7 Layout Accommodates Expansion |
|
||||
| `dir` faked with `text-align` and no `dir` attribute | P6 Text Direction is a Layout Primitive |
|
||||
| No RTL build until the first RTL locale ships | P6 Text Direction is a Layout Primitive |
|
||||
|
||||
## Relationship to Other Domains
|
||||
|
||||
- `domains/uiux/accessibility.md` — RTL support is an accessibility
|
||||
concern for non-Latin-script users; WCAG 2.1 AA requires that
|
||||
direction be set correctly.
|
||||
- `domains/uiux/components.md` — components are built with logical
|
||||
properties so they survive direction and expansion without per-
|
||||
locale overrides.
|
||||
- `domains/i18n/testing-i18n.md` — RTL coverage is an e2e-tier
|
||||
test; pseudo-locale mirroring surfaces direction bugs early.
|
||||
- `domains/i18n/locale-resources.md` — the `dir` is part of the
|
||||
locale's metadata, carried alongside the resource bundle.
|
||||
@@ -0,0 +1,142 @@
|
||||
# Testing i18n — Derived Rules
|
||||
|
||||
> Derives from `domains/i18n/first-principles.md`. Covers P8
|
||||
> (Pseudo-Locales Test Early) and the testing-discipline angle on
|
||||
> P3 (Resources External), P5 (Formatting Locale-Aware), P6 (Text
|
||||
> Direction), and P10 (Translation Versioned). Referenced by
|
||||
> `locale-resources.md` (missing-key detection) and `rtl-bidi.md`
|
||||
> (RTL coverage tier).
|
||||
|
||||
## Pseudo-Locales Test Early (P8 Pseudo-Locales Test Early)
|
||||
|
||||
- A pseudo-locale is a synthetic locale that transforms the source
|
||||
strings to surface i18n defects before real translations arrive.
|
||||
Three transforms cover the three defect classes:
|
||||
|
||||
| Pseudo-locale | Transform | Surfaces |
|
||||
|---------------|-----------|----------|
|
||||
| `en-XA` (accented) | `Wêlcômê tô thê çhêckôût` | Strings not extracted (raw source appears), encoding bugs |
|
||||
| `en-XB` (lengthened / "long") | `Wᴇʟᴄᴏᴍᴇ ᴛᴏ ᴛʜᴇ ᴄʜᴇᴄᴋᴏᴜᴛ──────` (~30% longer, bracketed) | Layout overflow, fixed widths, truncation |
|
||||
| `en-XC` (RTL-mirrored) | Source rendered with `dir="rtl"` and a Latin-in-RTL run | LTR-only layout assumptions, physical CSS properties |
|
||||
|
||||
- Pseudo-locale tests are cheap: they run against source strings, no
|
||||
translator involved, no string freeze required. A failing pseudo-
|
||||
locale run is a bug found at the cheapest possible point in the
|
||||
pipeline. Finding the same bug after real translation is a C5
|
||||
(Reversibility) violation: the fix now costs a re-translation.
|
||||
|
||||
## Pseudo-Locale → Testing Pyramid Mapping (IDEATE-28)
|
||||
|
||||
- The testing pyramid (`domains/testing/pyramid.md`) has three tiers;
|
||||
i18n tests map to each tier with a distinct signal. The mapping is
|
||||
deliberate: each tier catches a different class of defect, and
|
||||
skipping a tier leaves a blind spot.
|
||||
|
||||
| Pyramid Tier | i18n Test | Defect Caught | Tooling Shape |
|
||||
|--------------|-----------|---------------|---------------|
|
||||
| **Unit** | Missing-key detection | A key referenced in code but absent from the resource bundle; a key present in the source locale but missing from a target locale | Static scan over the resource bundle + code AST; runs per file, no runtime |
|
||||
| **Integration** | Snapshot per locale | Formatted output for a fixture input differs across locales in a way that breaks the contract (wrong plural, wrong date, overflow) | Render a known fixture through the formatter per locale; snapshot-diff against the recorded baseline |
|
||||
| **e2e** | RTL coverage | The app renders and is navigable in `dir="rtl"`; no layout overflow, no off-screen controls, no LTR-pinned anchors | Browser-driven run against the `en-XC` pseudo-locale (or a real RTL locale); assert on layout, not just text |
|
||||
|
||||
- Unit is the broad base (fast, runs on every commit), e2e is the
|
||||
narrow top (slow, runs on PR merge). Integration sits between.
|
||||
This mirrors `domains/testing/pyramid.md` exactly — i18n is not a
|
||||
special case; it is a domain that uses the same tiers.
|
||||
|
||||
## Unit Tier — Missing-Key Detection (P3 Resources External)
|
||||
|
||||
- A static scan compares the set of keys referenced in code against
|
||||
the keys present in each locale bundle. A key in code but not in
|
||||
`en-US` is a P3 violation (the string is not in the resource
|
||||
layer). A key in `en-US` but not in `ar-EG` is a coverage gap —
|
||||
the missing-key scan flags it before the locale ships.
|
||||
- Missing keys fail the build, not the runtime. A missing key that
|
||||
surfaces only when a user switches locale is a defect found in
|
||||
production, which is the most expensive place to find it.
|
||||
|
||||
```
|
||||
// tool output (illustrative)
|
||||
// missing-key scan
|
||||
[FAIL] ar-EG: key "checkout.cart.item_count" referenced in code,
|
||||
absent from ar-EG.json
|
||||
[FAIL] en-US: key "checkout.cart.total" referenced in Checkout.tsx:42,
|
||||
absent from en-US.json (not extracted)
|
||||
[PASS] en-US, ar-EG, de-DE, zh-Hans-CN: all other keys present
|
||||
```
|
||||
|
||||
## Integration Tier — Snapshot per Locale (P5 Formatting Locale-Aware)
|
||||
|
||||
- For a fixed fixture input, render the formatted output per locale
|
||||
and snapshot it. A change in the snapshot is either an intended
|
||||
change (new CLDR data, new copy) or a regression.
|
||||
- The snapshot is per locale, not per format string. The same
|
||||
fixture (`{ count: 1, currency: "EUR", date: 2024-11-07 }`)
|
||||
produces different snapshots for `en-US`, `de-DE`, `ar-EG` — and
|
||||
that difference is the assertion. A locale whose snapshot matches
|
||||
the source locale's is a red flag: the formatter is not actually
|
||||
locale-aware.
|
||||
|
||||
```
|
||||
// snapshot — checkout.cart (fixture: count=1, currency=EUR, date=2024-11-07)
|
||||
// en-US
|
||||
"1 item · €1,234.56 · 11/7/2024"
|
||||
// de-DE
|
||||
"1 Artikel · 1.234,56 € · 07.11.2024"
|
||||
// ar-EG
|
||||
"عنصر واحد · ١٬٢٣٤٫٥٦ € · ٧/١١/٢٠٢٤"
|
||||
```
|
||||
|
||||
- Snapshots are reviewed, not rubber-stamped. A snapshot diff that
|
||||
changes the plural form for `ar-EG` is either a CLDR update (verify)
|
||||
or a regression (revert).
|
||||
|
||||
## e2e Tier — RTL Coverage (P6 Text Direction is a Layout Primitive)
|
||||
|
||||
- A browser-driven run against `en-XC` (or a real RTL locale like
|
||||
`ar-EG`) asserts that the app is navigable in RTL: no overflow, no
|
||||
off-screen controls, no LTR-pinned anchors. The assertion is on
|
||||
layout, not on text — text correctness is the integration tier's
|
||||
job.
|
||||
- RTL e2e is the narrow top of the i18n pyramid: it is slow, it
|
||||
requires a browser, and it catches the defects the lower tiers
|
||||
cannot (the interaction of `dir` with the real layout engine). It
|
||||
runs on PR merge, not on every commit.
|
||||
|
||||
## Snapshot Discipline (P10 Translation Reversible and Versioned)
|
||||
|
||||
- Snapshots are versioned in git. A snapshot that changes because of
|
||||
a real translation update is a committed diff, reviewed like a
|
||||
code change. A snapshot that changes because of a regression is a
|
||||
`git revert`.
|
||||
- A snapshot that is regenerated and committed without review is a
|
||||
P10 violation: the snapshot is versioned but the provenance is
|
||||
opaque. The same discipline applies to snapshots as to resources
|
||||
(`locale-resources.md`).
|
||||
|
||||
## What Violates i18n Testing Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| First i18n test runs against real translations, not pseudo-locales | P8 Pseudo-Locales Test Early |
|
||||
| No missing-key scan — gaps surface only at runtime in production | P3 Resources are External, Not Inline |
|
||||
| Snapshot per locale that matches the source locale's snapshot | P5 Formatting is Locale-Aware |
|
||||
| No RTL e2e — "we'll test RTL when we ship an RTL locale" | P6 Text Direction is a Layout Primitive |
|
||||
| Snapshots regenerated and committed without review | P10 Translation is Reversible and Versioned |
|
||||
| i18n tests only at e2e (no unit/integration tier) | pyramid inversion — `domains/testing/pyramid.md` |
|
||||
| Pseudo-locale run skipped because "it's not a real locale" | P8 Pseudo-Locales Test Early |
|
||||
|
||||
## Relationship to Other Domains
|
||||
|
||||
- `domains/testing/pyramid.md` — the pseudo-locale → pyramid mapping
|
||||
mirrors this domain's unit / integration / e2e tiers exactly.
|
||||
- `domains/testing/fixtures.md` — locale fixtures (a fixed input
|
||||
rendered per locale) are the fixture shape for the integration
|
||||
tier.
|
||||
- `domains/i18n/locale-resources.md` — missing-key detection is the
|
||||
unit-tier scan over the resource bundle this doc defines.
|
||||
- `domains/i18n/formatting.md` — the integration-tier snapshot
|
||||
asserts against the formatter's output.
|
||||
- `domains/i18n/rtl-bidi.md` — the e2e tier exercises the layout
|
||||
rules this doc establishes.
|
||||
- `domains/uiux/accessibility.md` — RTL coverage is an a11y
|
||||
concern; an untested RTL build is an untested a11y surface.
|
||||
+25
-10
@@ -6,14 +6,14 @@
|
||||
|
||||
| Core Principle | Domains that derive from it | Count |
|
||||
|----------------|---------------------------|-------|
|
||||
| C1 Correctness | All 13 (v0.1: 11; v0.2: infrastructure-as-code, kubernetes) | Universal |
|
||||
| C2 Clarity | v0.1: uiux, api, data, testing, observability, errors, documentation, devops; v0.2: infrastructure-as-code, kubernetes | 10 |
|
||||
| C3 Simplicity | v0.1: security, data, testing, performance, documentation, concurrency, devops | 7 |
|
||||
| C4 Locality | v0.1: testing, concurrency; v0.2: infrastructure-as-code, kubernetes | 4 |
|
||||
| C5 Reversibility | v0.1: api, data, uiux, concurrency, devops; v0.2: infrastructure-as-code, kubernetes | 7 |
|
||||
| C6 Composability | v0.1: api, security, observability, errors, documentation, concurrency; v0.2: infrastructure-as-code, kubernetes | 8 |
|
||||
| C7 Observability | v0.1: api, data, testing, performance, observability, errors, devops; v0.2: infrastructure-as-code, kubernetes | 9 |
|
||||
| C8 Economy | v0.1: security, testing, performance, observability, concurrency; v0.2: kubernetes | 6 |
|
||||
| C1 Correctness | All 17 (v0.1: 11; v0.2: infrastructure-as-code, kubernetes; v0.3: gitops-operators, ai-ml, i18n, compliance) | Universal |
|
||||
| C2 Clarity | v0.1: uiux, api, data, testing, observability, errors, documentation, devops; v0.2: infrastructure-as-code, kubernetes; v0.3: gitops-operators, ai-ml, i18n, compliance | 14 |
|
||||
| C3 Simplicity | v0.1: security, data, testing, performance, documentation, concurrency, devops; v0.2: infrastructure-as-code; v0.3: gitops-operators, i18n, compliance | 11 |
|
||||
| C4 Locality | v0.1: testing, concurrency; v0.2: infrastructure-as-code, kubernetes; v0.3: gitops-operators, i18n | 6 |
|
||||
| C5 Reversibility | v0.1: api, data, uiux, concurrency, devops; v0.2: infrastructure-as-code, kubernetes; v0.3: gitops-operators, ai-ml, i18n, compliance | 11 |
|
||||
| C6 Composability | v0.1: api, security, observability, errors, documentation, concurrency; v0.2: infrastructure-as-code, kubernetes; v0.3: gitops-operators, ai-ml, i18n, compliance | 12 |
|
||||
| C7 Observability | v0.1: api, data, testing, performance, observability, errors, devops; v0.2: infrastructure-as-code, kubernetes; v0.3: gitops-operators, ai-ml, i18n, compliance | 13 |
|
||||
| C8 Economy | v0.1: security, testing, performance, observability, concurrency; v0.2: kubernetes; v0.3: gitops-operators, i18n, compliance | 9 |
|
||||
|
||||
## Interpretation
|
||||
|
||||
@@ -39,6 +39,10 @@
|
||||
| DevOps | C1, C2, C3, C5, C7 | Reproducibility + rollback |
|
||||
| Infrastructure as Code | C1, C2, C3, C4, C5, C6, C7 | Declarative + state + composition; broadest derivation alongside Concurrency |
|
||||
| Kubernetes | C1, C2, C4, C5, C6, C7, C8 | Declarative + reversibility + economy; broad derivation (7 C-rules) |
|
||||
| GitOps + Operators | C1, C2, C3, C4, C5, C6, C7, C8 | Source-of-truth + reconciliation + pull-locality + least privilege; broadest derivation (8 C-rules, tied with i18n) |
|
||||
| AI / ML | C1, C2, C5, C6, C7 | Reproducibility + lineage + serving observability |
|
||||
| i18n | C1, C2, C3, C4, C5, C6, C7, C8 | Locale + formatting + direction + reversibility; broadest derivation (8 C-rules) |
|
||||
| Compliance | C1, C2, C3, C5, C6, C7, C8 | Audit + policy-as-code + retention + posture |
|
||||
|
||||
## v0.2 Domain Coverage (per IDEATE-03 schema)
|
||||
|
||||
@@ -47,10 +51,21 @@
|
||||
| Infrastructure as Code | 10 | 4 (terraform, opentofu, state, modules) | ✓ | complete |
|
||||
| Kubernetes | 10 | 6 (workloads, networking, storage, rbac, helm, kustomize) | ✓ | complete |
|
||||
|
||||
## v0.3 Domain Coverage (per IDEATE-03 schema)
|
||||
|
||||
| Domain | P-count | Derived-doc-count | Manifest-listed | Status |
|
||||
|--------|---------|-------------------|-----------------|--------|
|
||||
| GitOps + Operators | 10 | 4 (argocd, flux, operators, progressive-delivery) | ✓ | complete |
|
||||
| AI / ML | 10 | 4 (data-versioning, model-evaluation, serving, monitoring-drift) | ✓ | complete |
|
||||
| i18n | 10 | 4 (locale-resources, formatting, rtl-bidi, testing-i18n) | ✓ | complete |
|
||||
| Compliance | 10 | 4 (audit-logs, data-retention, policy-as-code, evidence) | ✓ | complete |
|
||||
|
||||
## Gaps and Notes
|
||||
|
||||
- No domain derives from only one C-rule. The minimum is 4 (UI/UX: C1, C2, C3, C5, C7 — actually 5). Every domain is multi-rooted.
|
||||
- **Concurrency**, **Infrastructure as Code**, and **Kubernetes** are tied for the broadest derivation (7 C-rules each) — these domains touch the most core concerns.
|
||||
- **Concurrency**, **Infrastructure as Code**, and **Kubernetes** are tied for the broadest derivation among v0.1/v0.2 platform domains (7 C-rules each) — these domains touch the most core concerns.
|
||||
- **GitOps + Operators** and **i18n** are tied for the single broadest-derivation domain overall (8 C-rules each: C1–C8). GitOps adds C4 (pull-credential locality) alongside its source-of-truth/reconciliation/least-privilege derivation; i18n touches correctness, clarity, simplicity, locality, reversibility, composability, observability, and economy (expansion accommodation). This is consistent with both domains' cross-cutting nature.
|
||||
- **UI/UX** and **API** are the most user-facing; they emphasize C2 (Clarity) heavily.
|
||||
- **Security** is the only domain with explicit non-tradeable declarations; this promotes 8 of its rules to C1-equivalent per `core/conflict-resolution.md` §6.
|
||||
- **v0.2 expansion:** C4 (Locality) grew from 2 to 4 domains (added infrastructure-as-code state locality, kubernetes namespace blast-radius). C6 (Composability) grew from 6 to 8. The two new domains are broad-derivation domains (7 C-rules each), consistent with Concurrency's breadth.
|
||||
- **v0.2 expansion:** C4 (Locality) grew from 2 to 4 domains (added infrastructure-as-code state locality, kubernetes namespace blast-radius). C6 (Composability) grew from 6 to 8. The two new domains are broad-derivation domains (7 C-rules each), consistent with Concurrency's breadth.
|
||||
- **v0.3 expansion:** C3 (Simplicity) grew from 7 to 11 (added gitops-operators declarative simplicity, i18n flexible layout, compliance structural redaction). C4 (Locality) grew from 4 to 6 (added gitops-operators pull-credential locality, i18n resource/text-direction locality). C5 (Reversibility) grew from 7 to 11 (added all four v0.3 domains — gitops history, ai-ml reproducibility, i18n translation versioning, compliance append-only/retention). C6 (Composability) grew from 8 to 12. C7 (Observability) grew from 9 to 13 (added all four v0.3 domains — reconciliation, drift detection, format correctness, posture). C2 (Clarity) grew from 10 to 14. The v0.3 expansion broadens every non-universal C-rule's coverage, confirming the four new domains are cross-cutting and well-rooted.
|
||||
@@ -205,8 +205,68 @@ C5=Reversibility · C6=Composability · C7=Observability · C8=Economy
|
||||
| P9 Config and Secrets Sep | C2 | Clarity of configuration |
|
||||
| P10 Roll Forward, Roll Back | C5 | Reversibility of deploys |
|
||||
|
||||
## Coverage Summary (post-v0.2)
|
||||
## GitOps + Operators
|
||||
|
||||
- 13 domains (11 v0.1 + 2 v0.2: infrastructure-as-code, kubernetes)
|
||||
- 130 domain principles total (110 v0.1 + 20 v0.2)
|
||||
| GitOps Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Git is the Source of Truth | C1, C5 | Correctness of source; reversibility via history |
|
||||
| P2 Declarative Over Imperative | C2, C3 | Clarity of intent; simplicity of expression |
|
||||
| P3 Pull, Don't Push | C1, C4 | Correctness via security; locality of credentials |
|
||||
| P4 Continuous Reconciliation | C7, C1 | Observability of drift; correctness of converge loop |
|
||||
| P5 State is Immutable and Versioned | C5 | Reversibility through history |
|
||||
| P6 Operators Encode Domain Knowledge | C6, C2 | Composability of expertise; clarity of operations |
|
||||
| P7 Progressive Delivery is Reversible | C5, C1 | Reversibility of promotion; correctness of abort |
|
||||
| P8 Reconcile, Don't Mutate by Hand | C1, C7 | Correctness of source of truth; observability of drift |
|
||||
| P9 Failure is Observable and Surfaced | C7 | Observability of sync/rollout health |
|
||||
| P10 Least Privilege Reconciliation | C1, C8 | Correctness via security; economy of trust |
|
||||
|
||||
## AI / ML
|
||||
|
||||
| AI/ML Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Reproducibility is the First Class | C1, C5 | Correctness of runs; reversibility of reproduction |
|
||||
| P2 Data is Versioned, Not Just Code | C5, C7 | Reversibility of data; observability of dataset lineage |
|
||||
| P3 Lineage is Traceable End-to-End | C7, C1 | Observability of predictions; correctness of provenance |
|
||||
| P4 Evaluation is Defined Before Training | C1, C2 | Correctness of metrics; clarity of thresholds |
|
||||
| P5 Models are Versioned Artifacts | C5, C6 | Reversibility of model rollbacks; composability of registry |
|
||||
| P6 Serving is Observable | C7 | Observability of inference |
|
||||
| P7 Drift is Expected and Detected | C7, C1 | Observability of drift; correctness of detection |
|
||||
| P8 Inference Inputs are Validated | C1 | Correctness at serving boundary |
|
||||
| P9 Pipelines Compose, Notebooks Don't | C6, C2 | Composability of steps; clarity of contracts |
|
||||
| P10 Rollback Includes the Model | C5 | Reversibility at the model layer |
|
||||
|
||||
## i18n
|
||||
|
||||
| i18n Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Source Language is a Locale, Not the Default | C2, C1 | Clarity; correctness of localization model |
|
||||
| P2 Locale Identifiers are Standardized | C2, C6 | Clarity; composability of BCP 47 |
|
||||
| P3 Resources are External, Not Inline | C4, C6 | Locality of strings; composability of resources |
|
||||
| P4 Plural and Gender are Parameterized | C1, C6 | Correctness across locales; composability of message format |
|
||||
| P5 Formatting is Locale-Aware | C1, C7 | Correctness of formats; observability of format correctness |
|
||||
| P6 Text Direction is a Layout Primitive | C1, C4 | Correctness of RTL/bidi; locality of direction |
|
||||
| P7 Layout Accommodates Expansion | C8, C3 | Economy of rework; simplicity of flexible layout |
|
||||
| P8 Pseudo-Locales Test Early | C7, C5 | Observability of bugs early; reversibility of finding late |
|
||||
| P9 Images and Icons are Cultural | C1, C2 | Correctness; clarity of cultural meaning |
|
||||
| P10 Translation is Reversible and Versioned | C5 | Reversibility of localization changes |
|
||||
|
||||
## Compliance
|
||||
|
||||
| Compliance Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Audit Logs are Append-Only | C1, C5 | Correctness of audit; reversibility of immutable record |
|
||||
| P2 Every Significant Action is Logged | C7, C1 | Observability of actions; correctness of audit set |
|
||||
| P3 Retention is Policy, Not Storage | C5, C8 | Reversibility of lifecycle; economy of storage |
|
||||
| P4 Policy is Code | C6, C2 | Composability of policy; clarity of rules |
|
||||
| P5 Policy is Evaluated as a Gate | C1, C5 | Correctness of enforcement; reversibility of block |
|
||||
| P6 Evidence is Collected Continuously | C7, C3 | Observability of posture; simplicity of audit |
|
||||
| P7 Identity is Attributable | C1, C7 | Correctness of attribution; observability of subject |
|
||||
| P8 Subject Access is Honored | C1, C5 | Correctness of rights; reversibility of deletion/export |
|
||||
| P9 Secrets and Sensitive Data are Redacted in Audit | C1, C3 | Correctness via security; simplicity of structural redaction |
|
||||
| P10 Compliance Posture is Observable | C7, C1 | Observability of compliance; correctness of posture |
|
||||
|
||||
## Coverage Summary (post-v0.3)
|
||||
|
||||
- 17 domains (11 v0.1 + 2 v0.2: infrastructure-as-code, kubernetes; 4 v0.3: gitops-operators, ai-ml, i18n, compliance)
|
||||
- 170 domain principles total (110 v0.1 + 20 v0.2 + 40 v0.3)
|
||||
- Every domain P-rule traces to ≥1 core C-rule (C1–C8). No orphans.
|
||||
@@ -147,6 +147,56 @@ If the task touches a domain, run that domain's checklist:
|
||||
- [ ] Rollout history retained; rollback tested (P10)
|
||||
- [ ] Namespaces used to bound blast radius; not `default` in prod (P6)
|
||||
|
||||
### If GitOps + Operators (see `domains/gitops-operators/`)
|
||||
- [ ] Desired state lives in git, not in the cluster (P1)
|
||||
- [ ] Configuration is declarative, not imperative scripts (P2)
|
||||
- [ ] Reconciliation is pull-based; no external push credentials into the cluster (P3)
|
||||
- [ ] Reconciliation loop runs continuously; drift auto-corrected (P4)
|
||||
- [ ] Every change is a commit; history is the audit/rollback path (P5)
|
||||
- [ ] Operational knowledge encoded as CRDs/controllers, not runbooks (P6)
|
||||
- [ ] Progressive delivery (canary/blue-green) has a tested abort/rollback path (P7)
|
||||
- [ ] No manual `kubectl apply`/`kubectl edit` on GitOps-managed resources (P8)
|
||||
- [ ] Sync failures, health degradation, and rollout stalls emit status + notifications (P9)
|
||||
- [ ] Controller credentials scoped to reconciled namespaces/resources; no cluster-admin GitOps robot (P10)
|
||||
|
||||
### If AI / ML (see `domains/ai-ml/`)
|
||||
- [ ] Scope check: this is engineering discipline (data versioning, evaluation, serving, drift), NOT algorithm/model design (D-023) — reject algorithm-design content
|
||||
- [ ] Every training run is reproducible from pinned data + code + config + environment (P1)
|
||||
- [ ] Datasets, features, and splits are versioned artifacts with lineage; `git` alone is insufficient (P2)
|
||||
- [ ] Any deployed prediction traces back through model → training run → dataset → source (P3)
|
||||
- [ ] Metrics, splits, and thresholds declared a priori; no post-hoc metric cherry-picking (P4)
|
||||
- [ ] Models are pinned, immutable, registry-tracked artifacts; never "the latest" (P5)
|
||||
- [ ] Inference latency, throughput, input distributions, and prediction confidence are observed (P6)
|
||||
- [ ] Data drift, concept drift, and prediction drift are monitored; a drift signal is an incident (P7)
|
||||
- [ ] Inference inputs validated against the model's contract (schema, ranges, types); out-of-contract rejected (P8)
|
||||
- [ ] Training/serving flows are composable pipelines with explicit steps; notebooks not in production (P9)
|
||||
- [ ] Serving rollback restores the prior model artifact, not just the prior code (P10)
|
||||
|
||||
### If i18n (see `domains/i18n/`)
|
||||
- [ ] Source language treated as one locale among many, not the "neutral" default (P1)
|
||||
- [ ] Locale identifiers use BCP 47 tags; no ad-hoc locale codes (P2)
|
||||
- [ ] User-facing strings in locale resource files, not concatenated inline in code (P3)
|
||||
- [ ] Plural/gender/select use ICU MessageFormat (or equivalent); no `if (n == 1)` branching (P4)
|
||||
- [ ] Dates, times, numbers, currencies, units via ICU/CLDR/`Intl`; no hand-rolled formatters (P5)
|
||||
- [ ] RTL/bidi is a first-class layout concern; logical CSS properties (`start`/`end`) over physical (`left`/`right`) (P6)
|
||||
- [ ] Layouts accommodate translation expansion; no fixed pixel widths for text (P7)
|
||||
- [ ] Pseudo-locales (accented, lengthened, RTL-mirrored) used to test before real translations arrive (P8)
|
||||
- [ ] Icons, colors, and imagery reviewed for locale-sensitivity; no locale-bound symbols treated as universal (P9)
|
||||
- [ ] Resource files versioned; a bad translation is a rollback, not a hot-patch (P10)
|
||||
|
||||
### If Compliance (see `domains/compliance/`)
|
||||
- [ ] Scope check: framework-agnostic — no regulation-specific (GDPR/HIPAA/SOC2/PCI) content (D-024)
|
||||
- [ ] Audit records are immutable once written; deletion/mutation is itself an auditable incident (P1)
|
||||
- [ ] The set of auditable actions is defined a priori; "we forgot to log it" is a violation (P2)
|
||||
- [ ] Data lifetime is declared and enforced as policy; deletion at end-of-life is a feature (P3)
|
||||
- [ ] Compliance policy expressed in versioned, reviewable, testable code (OPA/Cedar/Kyverno/Sentinel), not spreadsheets/prose (P4)
|
||||
- [ ] Policy violations block before the action (admission/CI/CD-time), not after the audit (P5)
|
||||
- [ ] Evidence gathered as a byproduct of operation, not assembled manually at audit time (P6)
|
||||
- [ ] Every logged action traces to an authenticated principal; no shared/generic identities (P7)
|
||||
- [ ] Data-subject rights (access, export, deletion) are operations with defined contracts and audit trails (P8)
|
||||
- [ ] Audit logs do not leak secrets; redaction is structural, not opportunistic (P9)
|
||||
- [ ] System reports its own compliance state (drift from policy, open violations, retention status) (P10)
|
||||
|
||||
## Final Gate
|
||||
|
||||
- [ ] Have I read the relevant domain's first-principles?
|
||||
|
||||
+62
-1
@@ -163,4 +163,65 @@ When you see a pattern listed here, it is a defect. Cite the principle it violat
|
||||
|--------------|----------|-----|
|
||||
| Orphaned P-rule (a domain principle with no matrix row) | matrix completeness, C6 | Breaks the conflict-resolution arbiter; the rule has no core trace |
|
||||
| Deployable example artifact (standalone `.tf`/`.yaml` under `examples/`) | PROJECT.md "no runtime code", D-025 | Violates the docs-only contract; examples must be `.md` with fenced code |
|
||||
| Unlisted v0.2 doc (new doc not added to MANIFEST) | manifest rule | Not part of the framework by definition |
|
||||
| Unlisted v0.2 doc (new doc not added to MANIFEST) | manifest rule | Not part of the framework by definition |
|
||||
|
||||
## v0.3 Chaos Anti-Patterns (from IDEATE-20, IDEATE-24, IDEATE-25, IDEATE-27)
|
||||
|
||||
These are named, cross-cutting violations specific to the v0.3 domains. Reject on sight.
|
||||
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| GitOps push-pattern (external CI pushes manifests to the cluster instead of an in-cluster agent pulling from git) | gitops P3 Pull, Don't Push; C1, C4 | Inverts the source-of-truth flow; requires push credentials into the cluster; breaks the reconciliation model (IDEATE-24, D-042) |
|
||||
| i18n LTR-only assumption (layout assumes left-to-right; no `dir` attribute, physical CSS properties only) | i18n P6 Text Direction is a Layout Primitive; C1, C4 | Disqualifying for RTL/Bidi users; locale-correctness violation (IDEATE-25, D-043) |
|
||||
| AI/ML orphan-model (a deployed prediction endpoint whose model has no lineage trace — no record of training run, dataset, or version) | ai-ml P3 Lineage is Traceable End-to-End; C7, C1 | Unreviewable, unrollbackable; the model is an unattributed artifact (IDEATE-27, D-045) |
|
||||
| Compliance mutable audit log (audit records can be edited or deleted by an operator) | compliance P1 Audit Logs are Append-Only; C1, C5 | Destroys the audit trail; the audit log's value is immutability — mutation is itself an incident |
|
||||
|
||||
### v0.3 Deployable Artifact Types (IDEATE-20, D-020)
|
||||
|
||||
The following standalone file types are forbidden under `examples/` and elsewhere in the framework. Examples are `.md` files with fenced code only.
|
||||
|
||||
| Forbidden standalone artifact | Belongs in | Why |
|
||||
|-------------------------------|-----------|-----|
|
||||
| `.po` / `.pot` resource files | fenced code in `examples/good/`/`examples/bad/*.md` | Runtime localization artifact; violates docs-only contract |
|
||||
| `.rego` / `.cedar` / `.sentinel` policy files | fenced code in `examples/*.md` | Runtime policy artifact; violates docs-only contract |
|
||||
| Model artifacts (`.pkl`, `.onnx`, `.pt`, `.h5`, `.safetensors`) | fenced code + prose in `examples/*.md` | Runtime model artifact; violates docs-only contract |
|
||||
| Signed manifests as standalone files (`.sig`, `.att`, `.intoto.jsonl`) | fenced code in `examples/*.md` | Runtime attestation artifact; violates docs-only contract |
|
||||
| Standalone `.yaml` / `.tf` / `.sh` | fenced code in `examples/*.md` | (Carried forward from v0.2) Runtime deployable artifact |
|
||||
|
||||
## v0.3 Domain-Specific Anti-Patterns
|
||||
|
||||
### GitOps + Operators
|
||||
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Push-based deploy (external CI `kubectl apply` into the cluster) | P3 Pull, Don't Push | Inverts the model; requires push credentials; bypasses reconciliation |
|
||||
| Manual `kubectl apply`/`kubectl edit` on a GitOps-managed resource | P8 Reconcile, Don't Mutate by Hand | Unreconciled drift; the next loop overwrites it — silent and unattributed |
|
||||
| `cluster-admin` GitOps robot (controller bound to cluster-admin) | P10 Least Privilege Reconciliation | Overbroad grant; blast radius = entire cluster |
|
||||
| No sync-failure notification (silent drift on health degradation) | P9 Failure is Observable and Surfaced | Silent drift is the bug the loop was supposed to surface |
|
||||
|
||||
### AI / ML
|
||||
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Unreproducible training run (unpinned data, code, config, or environment) | P1 Reproducibility is the First Class | Unreviewable; cannot debug, cannot rollback |
|
||||
| "Use the latest model" (serving points at `model:latest` instead of a pinned version) | P5 Models are Versioned Artifacts | Unversioned drift; rollback undefined |
|
||||
| Notebook in production (training/serving flow is a Jupyter notebook) | P9 Pipelines Compose, Notebooks Don't | No contracts, no composition, no reproducibility |
|
||||
| Orphan model (deployed prediction with no lineage trace) | P3 Lineage is Traceable End-to-End | Unattributed artifact; cannot trace to data/code (IDEATE-27) |
|
||||
|
||||
### i18n
|
||||
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Inline string concatenation (`"Hello, " + name + "!"` in code) | P3 Resources are External, Not Inline | Not extractable; breaks translations; word-order differs per locale |
|
||||
| `if (n == 1)` plural branching (hand-rolled plural logic) | P4 Plural and Gender are Parameterized | Wrong for Arabic, Russian, Polish; ICU MessageFormat handles plurals |
|
||||
| LTR-only layout (no `dir` attribute, physical CSS `left`/`right`) | P6 Text Direction is a Layout Primitive | Disqualifying for RTL/Bidi (IDEATE-25) |
|
||||
| Hand-rolled date/number formatter (`new Date().toString()`, manual string formatting) | P5 Formatting is Locale-Aware | Locale-incorrect; ignores ICU/CLDR |
|
||||
|
||||
### Compliance
|
||||
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Mutable audit log (operator can `UPDATE`/`DELETE` audit records) | P1 Audit Logs are Append-Only | Destroys the audit trail; mutation is itself an incident |
|
||||
| Shared/generic identity in audit (`admin` or `system` as the actor for all actions) | P7 Identity is Attributable | No attribution; no accountability; cannot investigate |
|
||||
| Secret leaked in audit log (request body or token captured in an audit event) | P9 Secrets and Sensitive Data are Redacted in Audit | Audit log becomes a secret exfiltration channel |
|
||||
| Manual evidence assembly at audit time (scramble to collect logs/scans/attestations on demand) | P6 Evidence is Collected Continuously | Audit-unready; evidence gathered under pressure is incomplete and unreliable |
|
||||
@@ -79,6 +79,56 @@ Run the relevant domain section from `agent-checklist.md` (UI/UX, API, Security,
|
||||
- [ ] Are ConfigMaps and Secrets separate?
|
||||
- [ ] Is the rollback path tested, not assumed?
|
||||
|
||||
### If GitOps + Operators
|
||||
- [ ] Is desired state sourced from git, not from the cluster?
|
||||
- [ ] Is configuration declarative, not imperative scripts?
|
||||
- [ ] Is reconciliation pull-based (no external push credentials into the cluster)?
|
||||
- [ ] Does the reconciliation loop run continuously and auto-correct drift?
|
||||
- [ ] Is every change a commit, with history as the audit/rollback path?
|
||||
- [ ] Is operational knowledge encoded as CRDs/controllers, not runbooks humans must remember?
|
||||
- [ ] Does progressive delivery (canary/blue-green) have a tested abort/rollback path?
|
||||
- [ ] Are there manual `kubectl apply`/`kubectl edit` on GitOps-managed resources? (flag as incident)
|
||||
- [ ] Do sync failures, health degradation, and rollout stalls emit status + notifications?
|
||||
- [ ] Are controller credentials scoped to reconciled namespaces/resources (no cluster-admin GitOps robot)?
|
||||
|
||||
### If AI / ML
|
||||
- [ ] Scope check: is this engineering discipline (data versioning, evaluation, serving, drift), NOT algorithm/model design? (D-023 — reject algorithm-design content)
|
||||
- [ ] Is every training run reproducible from pinned data + code + config + environment?
|
||||
- [ ] Are datasets, features, and splits versioned artifacts with lineage (not just `git`)?
|
||||
- [ ] Can any deployed prediction trace back through model → training run → dataset → source?
|
||||
- [ ] Are metrics, splits, and thresholds declared a priori (no post-hoc metric cherry-picking)?
|
||||
- [ ] Are models pinned, immutable, registry-tracked artifacts (never "the latest")?
|
||||
- [ ] Is inference observable (latency, throughput, input distributions, prediction confidence)?
|
||||
- [ ] Are data drift, concept drift, and prediction drift monitored (drift signal = incident)?
|
||||
- [ ] Are inference inputs validated against the model's contract (schema, ranges, types)?
|
||||
- [ ] Are training/serving flows composable pipelines (not notebooks in production)?
|
||||
- [ ] Does serving rollback restore the prior model artifact, not just the prior code?
|
||||
|
||||
### If i18n
|
||||
- [ ] Is the source language treated as one locale among many, not the "neutral" default?
|
||||
- [ ] Do locale identifiers use BCP 47 tags (no ad-hoc locale codes)?
|
||||
- [ ] Are user-facing strings in locale resource files (not concatenated inline in code)?
|
||||
- [ ] Do plural/gender/select use ICU MessageFormat (no `if (n == 1)` branching)?
|
||||
- [ ] Are dates, times, numbers, currencies, units formatted via ICU/CLDR/`Intl` (no hand-rolled formatters)?
|
||||
- [ ] Is RTL/bidi a first-class layout concern (logical CSS properties over physical)?
|
||||
- [ ] Do layouts accommodate translation expansion (no fixed pixel widths for text)?
|
||||
- [ ] Are pseudo-locales used to test before real translations arrive?
|
||||
- [ ] Are icons, colors, and imagery reviewed for locale-sensitivity?
|
||||
- [ ] Are resource files versioned (bad translation = rollback, not hot-patch)?
|
||||
|
||||
### If Compliance
|
||||
- [ ] Scope check: is this framework-agnostic (no regulation-specific GDPR/HIPAA/SOC2/PCI content)? (D-024)
|
||||
- [ ] Are audit records immutable once written (deletion/mutation is itself an auditable incident)?
|
||||
- [ ] Is the set of auditable actions defined a priori ("we forgot to log it" is a violation)?
|
||||
- [ ] Is data lifetime declared and enforced as policy (deletion at end-of-life is a feature)?
|
||||
- [ ] Is compliance policy expressed in versioned, reviewable, testable code (not spreadsheets/prose)?
|
||||
- [ ] Do policy violations block before the action (admission/CI/CD-time, not after the audit)?
|
||||
- [ ] Is evidence gathered as a byproduct of operation (not assembled manually at audit time)?
|
||||
- [ ] Does every logged action trace to an authenticated principal (no shared/generic identities)?
|
||||
- [ ] Are data-subject rights (access, export, deletion) operations with defined contracts and audit trails?
|
||||
- [ ] Do audit logs avoid leaking secrets (redaction is structural, not opportunistic)?
|
||||
- [ ] Does the system report its own compliance state (drift from policy, open violations, retention status)?
|
||||
|
||||
## Review Etiquette
|
||||
|
||||
- **Comment, don't command.** "This could be X" not "Change this to X."
|
||||
|
||||
Reference in New Issue
Block a user