@thebassclef/lite 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +85 -4
- package/dist/cli.cjs +1610 -0
- package/dist/cli.js +1587 -0
- package/dist/index.cjs +4 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/package.json +51 -3
- package/substrate/.bassclef/lite-manifest.json +1794 -0
- package/substrate/.claude/agents/architect.md +70 -0
- package/substrate/.claude/agents/builder.md +114 -0
- package/substrate/.claude/agents/designer.md +156 -0
- package/substrate/.claude/agents/reviewer.md +88 -0
- package/substrate/.claude/hooks/artifact-ingestion-gate.sh +357 -0
- package/substrate/.claude/hooks/assert-verify-steering.sh +77 -0
- package/substrate/.claude/hooks/bassclef-source-config-validate.sh +215 -0
- package/substrate/.claude/hooks/bassclef-sync.sh +634 -0
- package/substrate/.claude/hooks/compound-noun-scrub.sh +292 -0
- package/substrate/.claude/hooks/kiss-expansion-inject.sh +69 -0
- package/substrate/.claude/hooks/plain-english-steering.sh +156 -0
- package/substrate/.claude/hooks/post-skill-friction-check.sh +177 -0
- package/substrate/.claude/hooks/post-skill-telemetry.sh +62 -0
- package/substrate/.claude/hooks/pre-build-gate.sh +511 -0
- package/substrate/.claude/hooks/pre-commit-gate.sh +452 -0
- package/substrate/.claude/hooks/session-end.sh +433 -0
- package/substrate/.claude/hooks/session-reflection.sh +303 -0
- package/substrate/.claude/hooks/skill-body-grade-gate.sh +219 -0
- package/substrate/.claude/hooks/skill-body-intent-drift.sh +107 -0
- package/substrate/.claude/hooks/skill-step-list-check.sh +171 -0
- package/substrate/.claude/hooks/state-validate.sh +271 -0
- package/substrate/.claude/hooks/substrate-clarity-gate.sh +1110 -0
- package/substrate/.claude/hooks/temperance-gate.sh +147 -0
- package/substrate/.claude/hooks/testing-tier-enforce.sh +233 -0
- package/substrate/.claude/hooks/turn-prose-grade-measure.sh +219 -0
- package/substrate/.claude/hooks/turn-prose-kiss-check.sh +434 -0
- package/substrate/.claude/hooks/vocabulary-migration-check.sh +171 -0
- package/substrate/.claude/hooks/whereami-utc-gate.sh +142 -0
- package/substrate/.claude/luminaries/alan-cooper.md +170 -0
- package/substrate/.claude/luminaries/alistair-cockburn.md +140 -0
- package/substrate/.claude/luminaries/amazon-pr-faq.md +34 -0
- package/substrate/.claude/luminaries/ash-maurya.md +121 -0
- package/substrate/.claude/luminaries/bill-buxton.md +210 -0
- package/substrate/.claude/luminaries/charles-sanders-peirce.md +150 -0
- package/substrate/.claude/luminaries/david-ogilvy.md +192 -0
- package/substrate/.claude/luminaries/don-norman.md +173 -0
- package/substrate/.claude/luminaries/edward-tufte.md +179 -0
- package/substrate/.claude/luminaries/eric-evans.md +160 -0
- package/substrate/.claude/luminaries/frederick-brooks.md +50 -0
- package/substrate/.claude/luminaries/gang-of-four.md +157 -0
- package/substrate/.claude/luminaries/jason-fried-dhh.md +46 -0
- package/substrate/.claude/luminaries/jesse-james-garrett.md +154 -0
- package/substrate/.claude/luminaries/john-ousterhout.md +94 -0
- package/substrate/.claude/luminaries/karl-popper.md +132 -0
- package/substrate/.claude/luminaries/kent-beck.md +168 -0
- package/substrate/.claude/luminaries/linus-torvalds.md +218 -0
- package/substrate/.claude/luminaries/martin-fowler.md +164 -0
- package/substrate/.claude/luminaries/michael-feathers.md +125 -0
- package/substrate/.claude/luminaries/michael-nygard.md +118 -0
- package/substrate/.claude/luminaries/robert-c-martin.md +164 -0
- package/substrate/.claude/luminaries/saltzer-schroeder.md +194 -0
- package/substrate/.claude/luminaries/sophia-prater.md +193 -0
- package/substrate/.claude/luminaries/stephen-toulmin.md +131 -0
- package/substrate/.claude/rules/artifact-ingestion.md +179 -0
- package/substrate/.claude/rules/assert-only-after-verify.md +137 -0
- package/substrate/.claude/rules/blocked-items.md +146 -0
- package/substrate/.claude/rules/branching.md +28 -0
- package/substrate/.claude/rules/commit-conventions.md +22 -0
- package/substrate/.claude/rules/context-engineering.md +202 -0
- package/substrate/.claude/rules/context-management.md +85 -0
- package/substrate/.claude/rules/destructive-operations.md +69 -0
- package/substrate/.claude/rules/diagnosis.md +38 -0
- package/substrate/.claude/rules/guardrails.md +73 -0
- package/substrate/.claude/rules/identifier-leak-prevention.md +104 -0
- package/substrate/.claude/rules/lite-manifest-schema-change-discipline.md +98 -0
- package/substrate/.claude/rules/oo-ad-entry-point.md +117 -0
- package/substrate/.claude/rules/operator-facing-prose.md +196 -0
- package/substrate/.claude/rules/plain-english-discipline.md +154 -0
- package/substrate/.claude/rules/pr-body-shape.md +317 -0
- package/substrate/.claude/rules/pr-strategy.md +167 -0
- package/substrate/.claude/rules/sdlc-gates.md +149 -0
- package/substrate/.claude/rules/session-artifacts.md +236 -0
- package/substrate/.claude/rules/state-schema-validation.md +162 -0
- package/substrate/.claude/rules/whereami-load-bearing.md +202 -0
- package/substrate/.claude/rules/writing-craft-discipline.md +92 -0
- package/substrate/.claude/skills/build/SKILL.md +640 -0
- package/substrate/.claude/skills/decompose/SKILL.md +280 -0
- package/substrate/.claude/skills/diagnose/SKILL.md +297 -0
- package/substrate/.claude/skills/howdoi/SKILL.md +105 -0
- package/substrate/.claude/skills/ia-model/SKILL.md +108 -0
- package/substrate/.claude/skills/interaction-design/SKILL.md +112 -0
- package/substrate/.claude/skills/interpret-input/SKILL.md +180 -0
- package/substrate/.claude/skills/kiss/SKILL.md +449 -0
- package/substrate/.claude/skills/launch/SKILL.md +924 -0
- package/substrate/.claude/skills/lean-canvas/SKILL.md +332 -0
- package/substrate/.claude/skills/longrun/SKILL.md +426 -0
- package/substrate/.claude/skills/luminary/SKILL.md +481 -0
- package/substrate/.claude/skills/ogilvy-writing-audit/SKILL.md +177 -0
- package/substrate/.claude/skills/onboard-repo/SKILL.md +2190 -0
- package/substrate/.claude/skills/pattern-review/SKILL.md +99 -0
- package/substrate/.claude/skills/personas/SKILL.md +207 -0
- package/substrate/.claude/skills/promote/SKILL.md +283 -0
- package/substrate/.claude/skills/requirement/SKILL.md +98 -0
- package/substrate/.claude/skills/retro/SKILL.md +117 -0
- package/substrate/.claude/skills/riff/SKILL.md +114 -0
- package/substrate/.claude/skills/roadmap-reconcile/SKILL.md +163 -0
- package/substrate/.claude/skills/session-end/SKILL.md +309 -0
- package/substrate/.claude/skills/session-log/SKILL.md +299 -0
- package/substrate/.claude/skills/skills/SKILL.md +228 -0
- package/substrate/.claude/skills/spec/SKILL.md +105 -0
- package/substrate/.claude/skills/sprint/SKILL.md +392 -0
- package/substrate/.claude/skills/stage/SKILL.md +384 -0
- package/substrate/.claude/skills/state-a-problem/SKILL.md +185 -0
- package/substrate/.claude/skills/temperance/SKILL.md +108 -0
- package/substrate/.claude/skills/use-case/SKILL.md +417 -0
- package/substrate/.claude/skills/user-stories/SKILL.md +268 -0
- package/substrate/.claude/skills/value-prop/SKILL.md +251 -0
- package/substrate/.claude/skills/verify/SKILL.md +160 -0
- package/substrate/.claude/skills/whereami/SKILL.md +307 -0
- package/substrate/AGENTS.md +79 -0
- package/substrate/CLAUDE-lite.md +85 -0
- package/substrate/CODE_OF_CONDUCT.md +28 -0
- package/substrate/CONTRIBUTING.md +177 -0
- package/substrate/README.md +173 -0
- package/substrate/SECURITY.md +19 -0
- package/substrate/architecture/decisions/ADR-029-release-pipeline.md +79 -0
- package/substrate/architecture/decisions/ADR-031-non-breaking-changes-adopter-discipline.md +139 -0
- package/substrate/architecture/decisions/ADR-032-adopter-sync-dispatcher-architecture.md +192 -0
- package/substrate/architecture/decisions/ADR-039-release-tagging-scheme.md +145 -0
- package/substrate/architecture/decisions/ADR-040-planning-skill-vocabulary-and-lite-profile.md +155 -0
- package/substrate/architecture/decisions/ADR-044-unified-skill-body-template.md +162 -0
- package/substrate/lib/clean-artifacts-sweep.sh +112 -0
- package/substrate/lib/code-comment-discipline.sh +144 -0
- package/substrate/lib/composer-preflight.sh +459 -0
- package/substrate/lib/hook-inject.sh +255 -0
- package/substrate/lib/luminary-pick.sh +96 -0
- package/substrate/lib/output-discipline.sh +143 -0
- package/substrate/lib/rewrite-check.sh +214 -0
- package/substrate/lib/state.sh +1372 -0
- package/substrate/lib/telemetry.sh +205 -0
- package/substrate/lib/tier-check.sh +138 -0
- package/substrate/lib/tier-dependency-audit.sh +933 -0
- package/substrate/presence/install/bassclef-hook-connect.sh +178 -0
- package/substrate/presence/install/bassclef-sync.dispatcher.template.sh +841 -0
- package/substrate/presence/install/bassclef-sync.template.sh +2076 -0
- package/substrate/presence/install/schedule-auto-save.cron.sh +88 -0
- package/substrate/presence/install/schedule-auto-save.taskscheduler.md +122 -0
- package/substrate/scripts/aggregate-telemetry.sh +217 -0
- package/substrate/scripts/analyze-tier-dependencies.sh +239 -0
- package/substrate/scripts/generate-lite-manifest.sh +489 -0
- package/substrate/scripts/generate-tier-manifest.sh +28 -0
- package/substrate/scripts/intent-drift-check.sh +456 -0
- package/substrate/scripts/lite-manifest-drift-check.sh +146 -0
- package/substrate/scripts/render-lite-manifest-doc.sh +150 -0
- package/substrate/standards/bassclef-configs-schema.md +232 -0
- package/substrate/standards/bassclef-evolution.md +143 -0
- package/substrate/standards/bassclef-internal-jargon.md +244 -0
- package/substrate/standards/bassclef-managed-sentinel.md +96 -0
- package/substrate/standards/bassclef-source-config.md +228 -0
- package/substrate/standards/deferred-actions-schema.md +204 -0
- package/substrate/standards/docs-sync-allowlist.md +76 -0
- package/substrate/standards/graceful-exit.md +227 -0
- package/substrate/standards/hook-idempotency.md +102 -0
- package/substrate/standards/lite-manifest-schema-changes.md +75 -0
- package/substrate/standards/luminary-problem-patterns.md +481 -0
- package/substrate/standards/ogilvy-writing-rules.md +225 -0
- package/substrate/standards/opener-discipline.md +96 -0
- package/substrate/standards/operator-facing-prose-discipline.md +201 -0
- package/substrate/standards/project-directory-layout.md +276 -0
- package/substrate/standards/release-tagging.md +137 -0
- package/substrate/standards/sdlc-compliance.md +286 -0
- package/substrate/standards/secrets-lifecycle.md +210 -0
- package/substrate/standards/security-scanner-adapter.md +145 -0
- package/substrate/standards/skill-output-discipline.md +90 -0
- package/substrate/standards/state-spine.md +511 -0
- package/substrate/standards/tech-stack-config.md +109 -0
- package/substrate/standards/tier-dependency-analysis.md +167 -0
- package/substrate/standards/tier-runtime-deps/lite.md +57 -0
- package/substrate/standards/tier-tag-schema.md +155 -0
- package/substrate/standards/whereami-schema.md +301 -0
- package/substrate/standards/writing-guide.md +213 -0
- package/substrate/templates/deferred-action-template.md +45 -0
- package/substrate/templates/whereami-template.md +101 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
name: Michael Feathers
|
|
4
|
+
slug: michael-feathers
|
|
5
|
+
claimed_by: [builder, reviewer]
|
|
6
|
+
referenced_by_skills: [verify, pattern-review]
|
|
7
|
+
primary_domain: engineering
|
|
8
|
+
depth: full
|
|
9
|
+
solves_problems_brownfield:
|
|
10
|
+
- "Codebase has no tests; any change risks unknown breakage across the system"
|
|
11
|
+
- "A class or function can't be tested without instantiating half the application"
|
|
12
|
+
- "New behavior needs to be added adjacent to legacy code that can't be safely changed"
|
|
13
|
+
- "A module is deeply coupled to its dependencies (database, filesystem, clock); tests require real infrastructure"
|
|
14
|
+
solves_problems_greenfield:
|
|
15
|
+
- "Designing a new module that will be introduced to a legacy system; seams must be planned from the start"
|
|
16
|
+
- "Building a service layer that wraps a third-party system; the wrap method must stay testable as the third-party evolves"
|
|
17
|
+
- "Planning a brownfield rescue effort; need a staged safety-net strategy before any refactoring begins"
|
|
18
|
+
- "Establishing the testing discipline a team will use as a system ages"
|
|
19
|
+
anti_patterns:
|
|
20
|
+
- "Greenfield systems with modern tooling and existing test culture — Feathers is the brownfield specialist"
|
|
21
|
+
- "Systems where a full rewrite is genuinely the right call and incremental safety-net building would slow the work"
|
|
22
|
+
- "Pure architectural redesigns where the code isn't the constraint"
|
|
23
|
+
sources:
|
|
24
|
+
- type: book
|
|
25
|
+
title: "Working Effectively with Legacy Code"
|
|
26
|
+
authors: ["Michael C. Feathers"]
|
|
27
|
+
year: 2004
|
|
28
|
+
url: "https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052"
|
|
29
|
+
isbn: "9780131177055"
|
|
30
|
+
methodology_covered:
|
|
31
|
+
- "Characterization tests — pin current behavior before touching legacy code"
|
|
32
|
+
- "Seam identification — find places where behavior can change without editing in place (preprocessor, link, object seams)"
|
|
33
|
+
- "Sprout method / sprout class — add new testable behavior adjacent to legacy code"
|
|
34
|
+
- "Wrap method / wrap class — change behavior by wrapping; original is untouched"
|
|
35
|
+
- "Legacy code definition — code without tests"
|
|
36
|
+
- type: website
|
|
37
|
+
title: "Michael Feathers blog"
|
|
38
|
+
authors: ["Michael Feathers"]
|
|
39
|
+
year: 2008
|
|
40
|
+
url: "https://michaelfeathers.silvrback.com/"
|
|
41
|
+
methodology_covered:
|
|
42
|
+
- "Ongoing writing on software design, legacy systems, and refactoring discipline"
|
|
43
|
+
verification_status: verified
|
|
44
|
+
description: "\"Legacy code is code without tests. Everything else is detail.\" [ref:1]"
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
# Michael Feathers
|
|
48
|
+
|
|
49
|
+
## Signature
|
|
50
|
+
|
|
51
|
+
*"Legacy code is code without tests. Everything else is detail."* [ref:1]
|
|
52
|
+
|
|
53
|
+
Author of **Working Effectively with Legacy Code** (2004) [ref:1]. The bassclef voice on bringing old, test-less, or tightly-coupled code under control without a rewrite. Where Beck starts with TDD on a clean slate, Feathers starts with the code that actually exists in production today.
|
|
54
|
+
|
|
55
|
+
## Core methods
|
|
56
|
+
|
|
57
|
+
- **Characterization tests** — Write tests that describe what the code currently does, not what it should do. Before touching anything, pin the current behavior. These tests are the safety net that makes refactoring safe. Only after pinning do you change behavior — and changing behavior means writing a failing test first. [ref:1]
|
|
58
|
+
- **Seam identification** — A seam is a place where you can change the program's behavior without editing in place. Seams come in three types: preprocessor seams (build-time), link seams (object-file substitution), and object seams (subclassing or interface injection). Finding seams is finding where you can insert a test double without rewriting the production code. [ref:1]
|
|
59
|
+
- **Sprout method / sprout class** — When you need to add new behavior to a function that is too risky to touch, add the new behavior in a new method or class that you CAN test. Wire the new code into the legacy code with minimal contact. The sprout is independently testable; the legacy code is preserved. [ref:1]
|
|
60
|
+
- **Wrap method / wrap class** — When you need to change a method's behavior, wrap it: create a new method (or class) that calls the original and adds or modifies behavior around it. The wrap is testable; the original is untouched. Like Decorator, but driven by safety rather than design. [ref:1]
|
|
61
|
+
|
|
62
|
+
## When to channel this luminary
|
|
63
|
+
|
|
64
|
+
When the codebase has real production code that lacks tests, or where tests exist but the dependencies are so tangled that changing anything is risky. Feathers dominates legacy system rescue: adding a feature to a 10-year-old module, extracting a service from a monolith, or incrementally stabilizing a system before a modernization effort begins. Pair with Fowler (refactoring catalog) once the safety net is in place.
|
|
65
|
+
|
|
66
|
+
INSTEAD of channeling Feathers when the team is starting fresh: use Beck's TDD from the first line of production code. Feathers handles the before-state that TDD assumes away.
|
|
67
|
+
|
|
68
|
+
## Stack translation
|
|
69
|
+
|
|
70
|
+
In Next.js + Prisma + TypeScript:
|
|
71
|
+
|
|
72
|
+
- **Characterization tests** — for a legacy API route, write integration tests against its actual behavior (inputs, outputs, side effects) before touching it. Use vitest + supertest or a test client. Pin the response shapes that downstream code depends on.
|
|
73
|
+
- **Seam identification** — TypeScript interfaces are the primary seam type. A service function that imports Prisma directly has no seam; extract an interface and inject via constructor or parameter. The seam enables swapping a real DB client for a test double.
|
|
74
|
+
- **Sprout method** — a 200-line server action that handles validation + database + notification: sprout the new validation logic into a standalone function tested in isolation. Wire it into the existing action with a single call. Ship both together.
|
|
75
|
+
- **Wrap class** — a third-party SDK (BizBuySell, an email provider) with no seam: create a thin wrapper class that implements your own interface and delegates to the SDK. Tests use a test double; production uses the wrapper. Swap SDKs later without touching callers.
|
|
76
|
+
|
|
77
|
+
## Anti-patterns it prevents
|
|
78
|
+
|
|
79
|
+
- **Edit and pray** — making a change to legacy code without tests and hoping nothing breaks. Feathers's characterization tests create the safety net before any edit, making each change verifiable rather than hopeful.
|
|
80
|
+
- **The big refactor** — attempting to redesign a legacy system comprehensively before adding tests. Feathers insists: tests come first, then refactoring. The comprehensive redesign without tests is just a different legacy system that happens to be new. [ref:1]
|
|
81
|
+
- **Dependency grabbing** — adding a new dependency to a legacy class because it was "easier." Each new dependency reduces the seam surface and makes future testing harder. Feathers's discipline: find the seam, inject the dependency, preserve optionality.
|
|
82
|
+
- **Sprout regression** — the new behavior works; the old behavior was accidentally changed. Characterization tests catch this: the old behavior is pinned; if any characterization test fails, something changed that was not intended to change.
|
|
83
|
+
|
|
84
|
+
## Worked example
|
|
85
|
+
|
|
86
|
+
For POA's legacy BizBuySell scraper module:
|
|
87
|
+
|
|
88
|
+
The scraper function is 300 lines; it fetches, parses, writes to the database, and sends a Slack notification. No tests. Before any change:
|
|
89
|
+
|
|
90
|
+
1. **Characterization test**: run the scraper against a recorded HTTP fixture; capture the database rows it writes and the Slack payload it sends. These are the characterization tests. Any change that alters these outputs is a signal, not a surprise.
|
|
91
|
+
|
|
92
|
+
2. **Seam identification**: the database write is not injectable — it calls Prisma directly. INSTEAD: extract a `ScrapeResultWriter` interface; pass it as a parameter in tests. The seam now exists.
|
|
93
|
+
|
|
94
|
+
3. **Sprout method**: the new requirement is deduplication before writing. INSTEAD of editing the 300-line function: sprout `deduplicateResults(incoming: ScrapeResult[], existing: ScrapeResult[]): ScrapeResult[]` — pure function, fully testable. Wire it into the legacy function with one line.
|
|
95
|
+
|
|
96
|
+
4. **Verify**: characterization tests still pass (old behavior preserved). New unit tests cover the deduplication logic independently.
|
|
97
|
+
|
|
98
|
+
## Sources
|
|
99
|
+
|
|
100
|
+
- *Working Effectively with Legacy Code* — Feathers, Prentice Hall (2004) [ref:1]
|
|
101
|
+
- michaelfeathers.silvrback.com — primary blog [ref:2]
|
|
102
|
+
- "Getting Empirical About Refactoring" — Feathers, various conference talks
|
|
103
|
+
|
|
104
|
+
## Related luminaries
|
|
105
|
+
|
|
106
|
+
- **Martin Fowler** — Refactoring is the next step after Feathers's safety net is in place; Fowler's catalog assumes tests exist, Feathers builds the before-state.
|
|
107
|
+
- **Kent Beck** — TDD is the end state Feathers helps teams reach incrementally.
|
|
108
|
+
- **Robert C. Martin** — Clean Code principles are the target; Feathers handles the path from legacy reality to that target.
|
|
109
|
+
- **Hunt & Thomas** — DRY and orthogonality are easier to maintain from the start; Feathers is the recovery discipline when they were not.
|
|
110
|
+
|
|
111
|
+
## Substrate-as-system anchor (bassclef bet 27c, 2026-06-27)
|
|
112
|
+
|
|
113
|
+
Feathers's "code without characterization tests is legacy code by definition" became the anchor citation for the bassclef substrate-as-system tenet (ADR-035). The bet 24c architect-review (2026-06-24) anchored on comprehension-side lenses (Brooks + Linus + Ishikawa + Deming) and missed the mechanism-fidelity class — "rule names mechanical enforcement that does not actually fire." The recursive Act phase (`architecture/audits/2026-06-27-audit-method-blind-spot.md`) named the lens-gap; Feathers's verification-side framing supplied the cure.
|
|
114
|
+
|
|
115
|
+
The bet 27c foundation ships:
|
|
116
|
+
- `standards/mechanism-fidelity.md` — audit method spec; Feathers-style characterization tests pin every rule's mechanism claim
|
|
117
|
+
- `lib/mechanism-fidelity.sh` — shared scanner that runs the 5-step verification chain
|
|
118
|
+
- `.claude/skills/audit-rule-mechanism-fidelity/SKILL.md` — operator-invocable audit (Feathers + Beck primary luminaries)
|
|
119
|
+
- `scripts/cold-adopter-harness-sync.sh` Shape d — per-PR gate (4 check classes; Feathers + Saltzer-Schroeder + Ousterhout primary)
|
|
120
|
+
- `.claude/rules/substrate-as-system.md` — auto-loaded tenet rule (cites Feathers as primary)
|
|
121
|
+
- `.claude/skills/architect-review/SKILL.md` — amendment makes Feathers + Beck + Saltzer-Schroeder verification-side primaries alongside Brooks + Linus comprehension-side primaries
|
|
122
|
+
|
|
123
|
+
Feathers's brownfield-rescue framing applies recursively at the substrate level: bassclef's own substrate had legacy mechanism-fidelity claims (108 claims classified by WU-6 first audit; 76 non-CLEAN). The cure bet (Bet C-pre-2) drives them to zero via the same Feathers discipline — characterization tests, seam identification, sprout/wrap patterns — applied to substrate hooks rather than application code.
|
|
124
|
+
|
|
125
|
+
The substrate as a system needs Feathers as much as application code does. This anchor citation reflects that.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
name: Michael Nygard
|
|
4
|
+
slug: michael-nygard
|
|
5
|
+
claimed_by: [architect]
|
|
6
|
+
referenced_by_skills: [architect-review, decompose]
|
|
7
|
+
primary_domain: architecture
|
|
8
|
+
depth: full
|
|
9
|
+
solves_problems_brownfield:
|
|
10
|
+
- "A single slow external API call hangs the entire application because the thread pool exhausts"
|
|
11
|
+
- "One failing service causes a cascade that takes down services with no direct dependency on it"
|
|
12
|
+
- "The application works fine locally and in staging but fails in production under real load patterns"
|
|
13
|
+
- "There is no defined behavior when an external integration is unavailable"
|
|
14
|
+
solves_problems_greenfield:
|
|
15
|
+
- "Designing a service that will integrate with external systems of unknown reliability"
|
|
16
|
+
- "Planning the failure modes of a distributed system before any code is written"
|
|
17
|
+
- "Building a system that continues operating (in degraded mode) when individual integrations fail"
|
|
18
|
+
- "Establishing the production-readiness criteria a system must meet before going live"
|
|
19
|
+
anti_patterns:
|
|
20
|
+
- "Single-process applications with no external integrations — stability patterns add overhead without value"
|
|
21
|
+
- "Batch systems that run to completion and exit — patterns designed for long-lived services"
|
|
22
|
+
- "Early-stage prototypes where production reliability is deliberately out of scope"
|
|
23
|
+
sources:
|
|
24
|
+
- type: book
|
|
25
|
+
title: "Release It! Design and Deploy Production-Ready Software"
|
|
26
|
+
authors: ["Michael T. Nygard"]
|
|
27
|
+
year: 2018
|
|
28
|
+
url: "https://pragprog.com/titles/mnee2/release-it-second-edition/"
|
|
29
|
+
isbn: "9781680502398"
|
|
30
|
+
methodology_covered:
|
|
31
|
+
- "Circuit breaker — stateful wrapper with Closed/Open/Half-Open states; prevents cascading failure"
|
|
32
|
+
- "Bulkhead — partition resources by concern; failure in one partition cannot exhaust shared resources"
|
|
33
|
+
- "Timeout — every remote call must have connect + read timeout values"
|
|
34
|
+
- "Fail fast — reject requests immediately when a known failure condition exists"
|
|
35
|
+
- "Stability anti-patterns — cascading failure, blocked threads, self-denial, integration point failures"
|
|
36
|
+
- type: book
|
|
37
|
+
title: "Release It! Design and Deploy Production-Ready Software (1st edition)"
|
|
38
|
+
authors: ["Michael T. Nygard"]
|
|
39
|
+
year: 2007
|
|
40
|
+
url: "https://pragprog.com/titles/mnee/release-it/"
|
|
41
|
+
methodology_covered:
|
|
42
|
+
- "Original stability patterns catalog"
|
|
43
|
+
- type: blog_post
|
|
44
|
+
title: "Documenting Architecture Decisions"
|
|
45
|
+
authors: ["Michael Nygard"]
|
|
46
|
+
year: 2011
|
|
47
|
+
url: "https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions"
|
|
48
|
+
methodology_covered:
|
|
49
|
+
- "ADR (Architecture Decision Record) format — Title, Status, Context, Decision, Consequences"
|
|
50
|
+
- "Originated the ADR format; widely adopted across software organizations"
|
|
51
|
+
verification_status: verified
|
|
52
|
+
description: "\"Everything fails. Every remote call can hang. Every integration point is a liability. Design your system to be stable in the presence of failure, not to assume failure away.\"…"
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
# Michael Nygard
|
|
56
|
+
|
|
57
|
+
## Signature
|
|
58
|
+
|
|
59
|
+
*"Everything fails. Every remote call can hang. Every integration point is a liability. Design your system to be stable in the presence of failure, not to assume failure away."* [ref:1]
|
|
60
|
+
|
|
61
|
+
Author of **Release It! Design and Deploy Production-Ready Software** (2nd ed 2018) [ref:1]. Coined the ADR (Architecture Decision Record) format in his 2011 blog post [ref:3]. The bassclef voice on production-grade software: the stability patterns that prevent cascading failures and the anti-patterns that cause them.
|
|
62
|
+
|
|
63
|
+
## Core methods
|
|
64
|
+
|
|
65
|
+
- **Circuit breaker** — A stateful wrapper around an integration point (external API, database, downstream service). Three states: Closed (normal operation), Open (fast-fail all calls without attempting contact), Half-Open (probe with one call to test recovery). When the failure rate exceeds a threshold, the circuit opens. Callers fail fast rather than hanging. After a timeout, one call is allowed through; if it succeeds, the circuit closes. [ref:1]
|
|
66
|
+
- **Bulkhead** — Partition resources by concern so that failure in one partition cannot exhaust resources needed by another. Named after the ship bulkhead that prevents water ingress in one compartment from sinking the ship. In software: separate thread pools for different integrations, separate connection pools for different databases, separate queue workers for different job types. [ref:1]
|
|
67
|
+
- **Timeout** — Every remote call must have a timeout. A call with no timeout can block a thread indefinitely. Two values needed: connect timeout (how long to wait for connection), read timeout (how long to wait for response). Timeouts must be tuned: too short creates unnecessary failures; too long creates unacceptable UX. [ref:1]
|
|
68
|
+
- **Fail fast** — When a system knows it cannot fulfill a request (circuit open, connection pool exhausted, downstream service unavailable), it should fail immediately with a clear error rather than accepting the request and failing slowly. Fail fast enables callers to retry or redirect; slow failure degrades the entire system. [ref:1]
|
|
69
|
+
|
|
70
|
+
## When to channel this luminary
|
|
71
|
+
|
|
72
|
+
When the system will integrate with any external dependency (databases, third-party APIs, downstream services, message queues) and failure modes of those integrations need explicit design. Nygard dominates production readiness reviews, architecture reviews before launch, and any system that must remain partially operational when integrations fail. Pair with Newman (service decomposition that creates the integration boundaries Nygard's patterns protect), Hohpe (integration patterns at the messaging layer), and Brown (ADRs for recording stability-pattern decisions).
|
|
73
|
+
|
|
74
|
+
INSTEAD of channeling Nygard for internal application logic: stability patterns are for integration points and external boundaries. Business logic, UI composition, and domain modeling are governed by Evans, Cockburn, and Cooper. Nygard's scope is the edges where your system meets systems you cannot control.
|
|
75
|
+
|
|
76
|
+
## Stack translation
|
|
77
|
+
|
|
78
|
+
In Next.js + Prisma + external APIs (BizBuySell, Anthropic, email providers):
|
|
79
|
+
|
|
80
|
+
- **Circuit breaker** — wrap each external API client (BizBuySell scraper, Anthropic enrichment, email delivery) in a circuit breaker. When the API is unavailable or slow, the circuit opens; callers receive a fast failure and can queue the work for retry. Libraries: `opossum` (Node.js) or a simple state machine per integration.
|
|
81
|
+
- **Bulkhead** — BullMQ workers for enrichment, for email, and for scraping use separate queues with separate concurrency limits. A spike in enrichment work cannot starve the email workers.
|
|
82
|
+
- **Timeout** — every `fetch()` call to an external API uses `AbortSignal.timeout()`. Anthropic API calls that exceed 30s abort; enrichment jobs that exceed 60s fail and are retried. Prisma connection pool has an explicit pool timeout.
|
|
83
|
+
- **Fail fast** — server actions that depend on an unavailable service return a clear `503` rather than hanging. The UI shows a retry state rather than a spinner.
|
|
84
|
+
- **ADRs** — every stability-pattern decision is recorded in `architecture/decisions/`: "ADR-009: circuit breaker for BizBuySell API — context, decision, consequences." [ref:3]
|
|
85
|
+
|
|
86
|
+
## Anti-patterns it prevents
|
|
87
|
+
|
|
88
|
+
- **Cascading failure** — a slow external API causes thread pool exhaustion, which causes queue buildup, which causes memory pressure, which causes the entire application to fail. Circuit breaker and bulkhead prevent this: failures stay contained within their integration boundary. [ref:1]
|
|
89
|
+
- **Blocked threads** — a database query or HTTP call with no timeout holds a thread indefinitely. Ten such calls block ten threads. The system accepts new requests it cannot service.
|
|
90
|
+
INSTEAD: set connect + read timeouts on every remote call; set a maximum wait in every queue consumer. Threads that time out release and become available for other work.
|
|
91
|
+
- **Self-denial** — the application triggers its own overload: a scheduled job fans out thousands of API calls simultaneously, the API rate-limits, all calls fail, the job retries them immediately, the rate-limit worsens. Circuit breaker + exponential backoff prevent the retry cascade. [ref:1]
|
|
92
|
+
- **Test harness gap** — the system was tested with a fast, reliable test double for every integration; production uses real integrations that are sometimes slow or unavailable. The failure paths were tested with mocks that never fail.
|
|
93
|
+
INSTEAD: include chaos tests (slow responses, connection drops, partial failures) in the integration test suite. The failure behavior must be verified, not assumed.
|
|
94
|
+
|
|
95
|
+
## Worked example
|
|
96
|
+
|
|
97
|
+
For POA's BizBuySell scraper integration:
|
|
98
|
+
|
|
99
|
+
**The failure mode without patterns**: BizBuySell returns 503 for 5-10 minutes. The scraper worker holds ten open HTTP connections waiting for responses that will not arrive; the BullMQ queue backs up; the worker crashes; manual intervention is required.
|
|
100
|
+
|
|
101
|
+
**With stability patterns**:
|
|
102
|
+
1. **Timeout**: every BizBuySell fetch has a 10-second timeout. After 10 seconds, the call fails with a timeout error.
|
|
103
|
+
2. **Circuit breaker**: after five consecutive timeouts within 60 seconds, the circuit opens. All subsequent scrape attempts fail fast. The circuit enters Half-Open after 60 seconds and allows one probe call through.
|
|
104
|
+
3. **Bulkhead**: the scraper uses a separate BullMQ queue (max 5 concurrent workers) from the enrichment queue (max 10 workers). A BizBuySell outage fills the scraper queue but cannot starve enrichment workers.
|
|
105
|
+
4. **ADR**: "ADR-010: BizBuySell API stability patterns — context: BizBuySell has 99.5% SLA (~43 hours downtime/year). We need scraping to be non-blocking during outages. Decision: circuit breaker with 5-failure threshold, 60s recovery. Consequences: scrape jobs may lag 60s during recovery; acceptable given the non-real-time nature of scrape data."
|
|
106
|
+
|
|
107
|
+
## Sources
|
|
108
|
+
|
|
109
|
+
- *Release It! Design and Deploy Production-Ready Software* — Nygard, Pragmatic Bookshelf (1st ed 2007, 2nd ed 2018) [ref:1][ref:2]
|
|
110
|
+
- "Documenting Architecture Decisions" — Nygard, cognitect.com blog (2011) — original ADR proposal [ref:3]
|
|
111
|
+
- thinkrelevance.com / cognitect.com — Nygard's primary writing venues (thinkrelevance.com was the predecessor domain to cognitect.com)
|
|
112
|
+
|
|
113
|
+
## Related luminaries
|
|
114
|
+
|
|
115
|
+
- **Sam Newman** — Microservices create the service boundaries where Nygard's stability patterns apply. Newman decomposes; Nygard hardens the resulting integration points.
|
|
116
|
+
- **Gregor Hohpe** — EIP messaging patterns operate at the same integration boundaries. Hohpe's dead letter channels and Nygard's circuit breakers are complementary: messaging + stability.
|
|
117
|
+
- **Simon Brown** — ADR format (Nygard's invention) is central to Brown's architecture communication approach. C4 documents the structure; ADRs record the stability decisions.
|
|
118
|
+
- **Alistair Cockburn** — Hexagonal architecture creates explicit integration ports; Nygard's stability patterns wrap those ports.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
name: Robert C. Martin (Uncle Bob)
|
|
4
|
+
slug: robert-c-martin
|
|
5
|
+
claimed_by: [architect, builder, reviewer]
|
|
6
|
+
referenced_by_skills: [pattern-review, decompose, architect-review]
|
|
7
|
+
primary_domain: engineering
|
|
8
|
+
depth: full
|
|
9
|
+
sources:
|
|
10
|
+
- type: book
|
|
11
|
+
title: "Clean Code: A Handbook of Agile Software Craftsmanship"
|
|
12
|
+
authors: ["Robert C. Martin"]
|
|
13
|
+
year: 2008
|
|
14
|
+
url: "https://www.pearson.com/en-us/subject-catalog/p/clean-code-a-handbook-of-agile-software-craftsmanship/P200000009044"
|
|
15
|
+
isbn: "9780132350884"
|
|
16
|
+
methodology_covered:
|
|
17
|
+
- "Functions should do one thing and do it well"
|
|
18
|
+
- "Meaningful names + small functions + no comments needed"
|
|
19
|
+
- "Code smells inventory + remediation patterns"
|
|
20
|
+
- "Boy Scout rule — leave the code cleaner than you found it"
|
|
21
|
+
- type: book
|
|
22
|
+
title: "Clean Architecture: A Craftsman's Guide to Software Structure and Design"
|
|
23
|
+
authors: ["Robert C. Martin"]
|
|
24
|
+
year: 2017
|
|
25
|
+
url: "https://www.pearson.com/en-us/subject-catalog/p/clean-architecture-a-craftsmans-guide-to-software-structure-and-design/P200000009454"
|
|
26
|
+
isbn: "9780134494166"
|
|
27
|
+
methodology_covered:
|
|
28
|
+
- "The Dependency Rule — source code dependencies always point inward"
|
|
29
|
+
- "Business rules never import frameworks, UI, or databases"
|
|
30
|
+
- "Boundaries between layers minimize ripple effects from external changes"
|
|
31
|
+
- type: book
|
|
32
|
+
title: "Agile Software Development: Principles, Patterns, and Practices"
|
|
33
|
+
authors: ["Robert C. Martin"]
|
|
34
|
+
year: 2002
|
|
35
|
+
url: "https://www.pearson.com/en-us/subject-catalog/p/agile-software-development-principles-patterns-and-practices/P200000009250"
|
|
36
|
+
isbn: "9780135974445"
|
|
37
|
+
methodology_covered:
|
|
38
|
+
- "SOLID principles — Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion"
|
|
39
|
+
- "Original codification of the SOLID acronym and each principle"
|
|
40
|
+
- type: book
|
|
41
|
+
title: "The Clean Coder: A Code of Conduct for Professional Programmers"
|
|
42
|
+
authors: ["Robert C. Martin"]
|
|
43
|
+
year: 2011
|
|
44
|
+
url: "https://www.pearson.com/en-us/subject-catalog/p/clean-coder-the-a-code-of-conduct-for-professional-programmers/P200000009422"
|
|
45
|
+
isbn: "9780137081073"
|
|
46
|
+
methodology_covered:
|
|
47
|
+
- "Professional discipline — saying no, estimating honestly, practicing"
|
|
48
|
+
- "Programming as profession with codes of conduct"
|
|
49
|
+
verification_status: verified
|
|
50
|
+
description: "\"Architecture is about intent, not frameworks."
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# Robert C. Martin (Uncle Bob)
|
|
54
|
+
|
|
55
|
+
## Signature
|
|
56
|
+
|
|
57
|
+
*"Architecture is about intent, not frameworks. The only way to go fast is to go well."*
|
|
58
|
+
|
|
59
|
+
Author of **Clean Code**, **Clean Architecture**, and **Clean Coder**.
|
|
60
|
+
Coined SOLID. The bassclef voice on code quality as a continuous
|
|
61
|
+
practice, not an end-of-project polish. We invoke Martin for SOLID,
|
|
62
|
+
boundaries, testability, and functions that do one thing.
|
|
63
|
+
|
|
64
|
+
## Core methods
|
|
65
|
+
|
|
66
|
+
- **SOLID principles** — Single Responsibility, Open/Closed, Liskov
|
|
67
|
+
Substitution, Interface Segregation, Dependency Inversion. Five
|
|
68
|
+
rules for OO design that survive decades of reapplication.
|
|
69
|
+
- **Clean Architecture / Dependency Rule** — Source-code dependencies
|
|
70
|
+
always point inward toward higher-level policy. Business rules don't
|
|
71
|
+
know about frameworks, UI, or databases. Frameworks are plugins.
|
|
72
|
+
- **Boundaries as architecture** — Architecture's job is defining which
|
|
73
|
+
parts of the system are volatile and which are stable, and drawing
|
|
74
|
+
boundaries between them. Boundaries allow deferral of decisions.
|
|
75
|
+
- **Functions do one thing** — Small, single-purpose functions with
|
|
76
|
+
descriptive names. Longest function should fit on a screen.
|
|
77
|
+
- **Tests as first-class design constraint** — Code not written for
|
|
78
|
+
testability produces code that can't be tested. TDD is the practice
|
|
79
|
+
that enforces this discipline from the start.
|
|
80
|
+
|
|
81
|
+
## When to channel this luminary
|
|
82
|
+
|
|
83
|
+
When the primary risk is code entropy — the product works today but
|
|
84
|
+
becomes unmaintainable tomorrow. Martin dominates for long-lived
|
|
85
|
+
codebases, teams that rotate, and systems that will be extended over
|
|
86
|
+
years. Pair with Fowler (refactoring, patterns) and Beck (TDD,
|
|
87
|
+
XP practices) for the engineering-quality triad.
|
|
88
|
+
|
|
89
|
+
Do NOT channel Martin for: throwaway prototypes, one-off scripts,
|
|
90
|
+
pure research code where cleanup isn't the point.
|
|
91
|
+
|
|
92
|
+
## Stack translation
|
|
93
|
+
|
|
94
|
+
In Next.js + Prisma + TypeScript:
|
|
95
|
+
|
|
96
|
+
- **Single Responsibility** → one component per concern; extract
|
|
97
|
+
`useXHook` when a component has multiple reasons to change.
|
|
98
|
+
- **Open/Closed** → extend via props/config, not by modifying existing
|
|
99
|
+
components. Strategy pattern for varying behavior.
|
|
100
|
+
- **Liskov** → interface contracts respected across implementations.
|
|
101
|
+
React component prop types that subtypes must honor.
|
|
102
|
+
- **Interface Segregation** → component prop types that don't force
|
|
103
|
+
consumers to provide unneeded props. Split large prop types into
|
|
104
|
+
focused ones.
|
|
105
|
+
- **Dependency Inversion** → depend on interfaces, not concrete
|
|
106
|
+
services. Facade pattern + interface → enables test stubs and
|
|
107
|
+
provider swaps (see `/decompose` Phase 1 Interface Registry).
|
|
108
|
+
- **Clean Architecture** → business logic in `lib/services/`;
|
|
109
|
+
adapters in `lib/adapters/`; controllers in `app/api/` + `app/actions/`;
|
|
110
|
+
frameworks stay at the edges.
|
|
111
|
+
|
|
112
|
+
## Anti-patterns it prevents
|
|
113
|
+
|
|
114
|
+
- **God components** — one component doing validation + business logic
|
|
115
|
+
+ DB call + side effects. Violates Single Responsibility.
|
|
116
|
+
- **Framework coupling in business logic** — Prisma imports in
|
|
117
|
+
domain-logic files, React hooks in pure computation. Violates
|
|
118
|
+
Dependency Rule.
|
|
119
|
+
- **Feature envy** — a class/component that reaches into another's
|
|
120
|
+
internals. Signal the responsibility is mis-assigned.
|
|
121
|
+
- **Untested code** — if it's not covered, it's broken by default.
|
|
122
|
+
Covered code is the minimum bar.
|
|
123
|
+
- **Clever code** — concise-but-unreadable constructs. Clean Code
|
|
124
|
+
requires clarity over brevity.
|
|
125
|
+
|
|
126
|
+
## Worked example
|
|
127
|
+
|
|
128
|
+
For POA's deal-scoring system:
|
|
129
|
+
|
|
130
|
+
Martin's approach:
|
|
131
|
+
- `ScoringService` (Pure Fabrication per GRASP + SRP per SOLID) —
|
|
132
|
+
orchestrates scoring, doesn't own score data
|
|
133
|
+
- `IScoringStrategy` interface — Open/Closed: new scoring rules as
|
|
134
|
+
new classes, not edits to existing code
|
|
135
|
+
- `WeightedScorer` / `BooleanScorer` / `MLScorer` — concrete strategies
|
|
136
|
+
- `ScoringController` as a thin server action — no business logic,
|
|
137
|
+
just wires strategy + service + response
|
|
138
|
+
- All of the above: unit tested. The service mocks the strategy; the
|
|
139
|
+
strategy is tested in isolation; the controller is tested with
|
|
140
|
+
service stub.
|
|
141
|
+
|
|
142
|
+
What Martin would NOT produce: a 400-line server action that directly
|
|
143
|
+
queries Prisma, applies scoring math inline, sends notifications, and
|
|
144
|
+
logs telemetry. That's every SOLID principle violated in one route.
|
|
145
|
+
|
|
146
|
+
## Sources
|
|
147
|
+
|
|
148
|
+
- *Clean Code* — Martin, Prentice Hall (2008)
|
|
149
|
+
- *Clean Architecture* — Martin, Prentice Hall (2017)
|
|
150
|
+
- *The Clean Coder* — Martin (2011)
|
|
151
|
+
- *Agile Software Development: Principles, Patterns, and Practices* — Martin (2002)
|
|
152
|
+
- cleancoders.com — video series
|
|
153
|
+
- blog.cleancoder.com
|
|
154
|
+
|
|
155
|
+
## Related luminaries
|
|
156
|
+
|
|
157
|
+
- **Martin Fowler** — Shared rigor; Fowler provides patterns + refactoring,
|
|
158
|
+
Martin provides principles. Pair always.
|
|
159
|
+
- **Kent Beck** — TDD discipline Martin endorses; Beck operational,
|
|
160
|
+
Martin principles.
|
|
161
|
+
- **Alistair Cockburn** — Hexagonal Architecture is a specific
|
|
162
|
+
implementation of Clean Architecture's boundary rule.
|
|
163
|
+
- **Gang of Four** — Martin recommends GoF patterns as implementation
|
|
164
|
+
vocabulary for SOLID principles.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
name: Jerome Saltzer & Michael Schroeder
|
|
4
|
+
slug: saltzer-schroeder
|
|
5
|
+
claimed_by: [architect, reviewer, builder, security-auditor]
|
|
6
|
+
referenced_by_skills: [security-review, architect-review, pattern-review, diagnose]
|
|
7
|
+
primary_domain: security
|
|
8
|
+
depth: full
|
|
9
|
+
sources:
|
|
10
|
+
- type: paper
|
|
11
|
+
title: "The Protection of Information in Computer Systems"
|
|
12
|
+
authors: ["Jerome H. Saltzer", "Michael D. Schroeder"]
|
|
13
|
+
year: 1975
|
|
14
|
+
edition: "Proceedings of the IEEE, vol. 63, no. 9, pp. 1278-1308"
|
|
15
|
+
url: "https://www.cs.virginia.edu/~evans/cs551/saltzer/"
|
|
16
|
+
methodology_covered:
|
|
17
|
+
- "Eight design principles for secure systems — economy of mechanism, fail-safe defaults, complete mediation, open design, separation of privilege, least privilege, least common mechanism, psychological acceptability"
|
|
18
|
+
- "The model of protected objects and authorized subjects; access matrix as the conceptual base"
|
|
19
|
+
- "Capability and ACL implementations of the access matrix"
|
|
20
|
+
- "Authentication vs authorization as separate concerns"
|
|
21
|
+
- type: paper
|
|
22
|
+
title: "Computer Security in the Real World"
|
|
23
|
+
authors: ["Butler W. Lampson"]
|
|
24
|
+
year: 2004
|
|
25
|
+
url: "https://www.microsoft.com/en-us/research/publication/computer-security-in-the-real-world/"
|
|
26
|
+
methodology_covered:
|
|
27
|
+
- "Modern reframing of the 1975 principles for distributed systems"
|
|
28
|
+
- "Gold standard — authenticate, authorize, audit — three legs every protection scheme requires"
|
|
29
|
+
verification_status: verified
|
|
30
|
+
description: "\"The principles are intended to guide the design of protection mechanisms..."
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
# Jerome Saltzer & Michael Schroeder
|
|
34
|
+
|
|
35
|
+
## Signature
|
|
36
|
+
|
|
37
|
+
*"The principles are intended to guide the design of protection mechanisms... they do not by themselves guarantee security, but they make insecurity easier to spot."*
|
|
38
|
+
|
|
39
|
+
MIT computer scientists. Their 1975 paper articulated eight design principles for secure systems that remain SENTINEL_the_bassclef_reference fifty years later. The principles predate the public internet, predate the web, predate every modern threat model — and still hold because they are about the *shape* of protection mechanisms, not the threats they defend against. When a new attack class lands, the analysis tool that maps it back to which principle was violated is almost always Saltzer & Schroeder.
|
|
40
|
+
|
|
41
|
+
The principles pair with later work: Lampson's "gold standard" (authenticate, authorize, audit) extends complete mediation; Anderson's *Security Engineering* puts every principle through industrial worked examples; OWASP's top-10 is a downstream symptom catalog whose root causes the principles name.
|
|
42
|
+
|
|
43
|
+
## Discipline anchor
|
|
44
|
+
|
|
45
|
+
**Designing protection mechanisms whose failure modes are visible at write-time.**
|
|
46
|
+
|
|
47
|
+
Saltzer & Schroeder fire on every moment a substrate component touches access, authority, scope, default behavior, or trust boundaries.
|
|
48
|
+
INSTEAD of waiting for an exploit to surface a missing check: the principles ask, at write-time, whether the mechanism's shape *makes* insecurity possible. Code that violates a principle is a defect even if no exploit has yet been demonstrated; code that conforms is a defense against threats the author did not foresee.
|
|
49
|
+
|
|
50
|
+
This luminary anchors the security lens for:
|
|
51
|
+
- `/security-review` — checks PRs against the 8 principles
|
|
52
|
+
- `/architect-review` — system-level design reviews look for principle violations at architectural boundaries
|
|
53
|
+
- `/pattern-review` — pattern selection (e.g., Decorator for logging, Strategy for auth) gets the principle lens
|
|
54
|
+
|
|
55
|
+
For usability-first questions, route to `@luminary don-norman` (cognitive load of mode prompts).
|
|
56
|
+
INSTEAD of citing Saltzer & Schroeder there: cite Norman. For API ergonomics, route to `@luminary john-ousterhout` (deep modules); for test mechanics, route to `@luminary kent-beck`.
|
|
57
|
+
|
|
58
|
+
## The eight principles
|
|
59
|
+
|
|
60
|
+
### 1. Economy of mechanism
|
|
61
|
+
|
|
62
|
+
Keep the design as small and simple as possible. Protection mechanisms are inspected for correctness by reasoning about their behavior; the smaller and simpler the mechanism, the more reliable the inspection.
|
|
63
|
+
|
|
64
|
+
Clever checks that require careful reasoning to verify carry inspection cost forever.
|
|
65
|
+
INSTEAD of building one: build a check that any reviewer can read once and accept.
|
|
66
|
+
|
|
67
|
+
**Bassclef example.** `.claude/rules/destructive-operations.md` instructs agents to pause before any state-modifying command. The rule is one page. A sophisticated heuristic engine would carry hidden states.
|
|
68
|
+
INSTEAD: a short rule + a hard list of trigger verbs. Small mechanism; correctness inspectable in a single read.
|
|
69
|
+
|
|
70
|
+
### 2. Fail-safe defaults
|
|
71
|
+
|
|
72
|
+
Base access decisions on permission rather than exclusion. The default situation is *lack of access*; the protection scheme identifies conditions under which access is permitted.
|
|
73
|
+
|
|
74
|
+
The shape "permit by default, deny on explicit refusal" produces a permanent risk surface — every new code path or branch type opens an unintended hole.
|
|
75
|
+
INSTEAD: deny by default, permit on explicit authorization.
|
|
76
|
+
|
|
77
|
+
**Bassclef example — the failure that motivated this luminary.** PR #1058's original auto-save design committed to whatever branch was active by default; the safety check was a list of refused branches (main, master, spike). Adding a new branch type (e.g., release/, hotfix/) silently worked — auto-save would commit there too. The default was "save" and the exceptions were "refuse." That violates fail-safe defaults.
|
|
78
|
+
INSTEAD: auto-save should DENY by default; an explicit allowlist (chore/, scratch/, feature/, fix/) permits. A new branch type is denied until explicitly added. The author of the new branch is forced to make the decision visible.
|
|
79
|
+
|
|
80
|
+
### 3. Complete mediation
|
|
81
|
+
|
|
82
|
+
Every access to every object must be checked for authority. The mechanism must intercept every attempt without exception.
|
|
83
|
+
|
|
84
|
+
A "check at the boundary and trust internal callers" shape produces a permanent silent-bypass surface — any internal path that skips the boundary skips the check.
|
|
85
|
+
INSTEAD: check at every access. The cost of one extra check is small; the cost of one missed check can be the whole system.
|
|
86
|
+
|
|
87
|
+
**Bassclef example.** `.claude/hooks/state-validate.sh` fires on every Edit / Write / MultiEdit to a state-spine path. Validating only at "official" entry points leaves alternative tool paths unmediated.
|
|
88
|
+
INSTEAD: the hook intercepts every write regardless of which tool made it. An agent that writes via a less-common tool path does not slip past the validator.
|
|
89
|
+
|
|
90
|
+
### 4. Open design
|
|
91
|
+
|
|
92
|
+
The protection mechanism's design should not depend on attacker ignorance of mechanism. Security through obscurity is weak; security through inspectable, public, well-reasoned design is strong.
|
|
93
|
+
|
|
94
|
+
"They will not know about this hidden flag" is a fragile assumption that erodes on the first leak.
|
|
95
|
+
INSTEAD: publish the flag, document the threat model, and rely on the mechanism (e.g., cryptographic key separation) for the actual protection.
|
|
96
|
+
|
|
97
|
+
**Bassclef example.** Bassclef's hooks, rules, and skills are all readable in the repo. Hiding which checks fire when would produce ambiguity about safety state.
|
|
98
|
+
INSTEAD: hooks announce themselves in chronicle, in BLOCKED banners, in trace logs. An agent that knows every hook's trigger condition cannot evade them by accident; the safety is in the hook firing on the trigger, not in the trigger being secret.
|
|
99
|
+
|
|
100
|
+
### 5. Separation of privilege
|
|
101
|
+
|
|
102
|
+
A protection mechanism that requires two independent checks is stronger than one that requires a single check. (The classic two-key safe analog.)
|
|
103
|
+
|
|
104
|
+
A single-signature unlock shape produces a single point of compromise.
|
|
105
|
+
INSTEAD: require operator confirmation AND a CI passing AND a manual approval gate.
|
|
106
|
+
|
|
107
|
+
**Bassclef example.** Production deploys require GitHub Environment with `required_reviewers` (per `.claude/rules/manual-prod-approval.md`) AND the deploy-guard.sh hook firing AND `gh pr merge` from an operator account. Three independent checks. A single compromise (e.g., agent's GitHub token) cannot deploy to production by itself.
|
|
108
|
+
|
|
109
|
+
### 6. Least privilege
|
|
110
|
+
|
|
111
|
+
Every program and every user of the system should operate using the least set of privileges necessary to complete the job.
|
|
112
|
+
|
|
113
|
+
Granting "read + write + delete + admin" because it is easier produces a permanent blast-radius surface.
|
|
114
|
+
INSTEAD: grant only what the specific operation requires. A read-only operation gets a read-only credential. A write to one path gets write access to that path only.
|
|
115
|
+
|
|
116
|
+
**Bassclef example — the failure that motivated this luminary.** PR #1058's original auto-save design granted auto-save the privilege to commit to *every* branch the agent visited. Auto-save needs the privilege to checkpoint work-in-progress on chore + scratch branches.
|
|
117
|
+
INSTEAD: scope auto-save to the branches that actually need it; deny everywhere else.
|
|
118
|
+
|
|
119
|
+
Bassclef token discipline (per memory `feedback_never_paste_tokens_in_chat`) applies the same principle.
|
|
120
|
+
INSTEAD of granting broad-scope tokens for narrow-scope work: use fine-grained PATs with minimal target-repo scope rather than full-org admin tokens.
|
|
121
|
+
|
|
122
|
+
### 7. Least common mechanism
|
|
123
|
+
|
|
124
|
+
Minimize the amount of mechanism shared by multiple users (or programs). Shared mechanisms are potential information paths and attack surfaces.
|
|
125
|
+
|
|
126
|
+
"One big config file for all secrets" produces cross-purpose attack surface.
|
|
127
|
+
INSTEAD: separate per-purpose stores. For multi-tenant systems: isolate per-tenant where the cost is reasonable rather than running one daemon for every tenant.
|
|
128
|
+
|
|
129
|
+
**Bassclef example.** `state/markers/temperance/` writes per-branch marker files instead of a single shared marker. A single global "temperance fired this session" flag would carry cross-scope trust.
|
|
130
|
+
INSTEAD: per-branch markers isolate the trust decision to its scope. A marker on branch A cannot grant temperance approval to branch B.
|
|
131
|
+
|
|
132
|
+
### 8. Psychological acceptability
|
|
133
|
+
|
|
134
|
+
Human interface to the protection mechanism must be designed for ease of use. If users find the mechanism annoying or confusing, they will work around it.
|
|
135
|
+
|
|
136
|
+
"Secure but unusable" produces operator workarounds — the most cryptographically perfect protection scheme is worthless if operators bypass it.
|
|
137
|
+
INSTEAD: secure AND usable.
|
|
138
|
+
|
|
139
|
+
**Bassclef example.** The `SKIP_*=1` override pattern across bassclef's hooks (e.g., `SKIP_TEMPERANCE=1`, `SKIP_DIAGNOSE=1`) provides an escape valve for cases where the gate is wrong. "Hard block with no override" produces operators who disable hooks entirely.
|
|
140
|
+
INSTEAD: hard block with a logged override. Operators bypass per-call when they understand the trade-off, and the trace surfaces the bypass.
|
|
141
|
+
|
|
142
|
+
Override usage is signal, not failure.
|
|
143
|
+
INSTEAD of treating override usage as failure: track override frequency. If a hook is overridden >10% of the time, the hook is mistargeted — psychological acceptability is failing and the hook needs redesign.
|
|
144
|
+
|
|
145
|
+
## Stack translation — applying the principles to bassclef's bash hooks
|
|
146
|
+
|
|
147
|
+
Bassclef's hooks are bash scripts under `.claude/hooks/*.sh`. The eight principles map directly to bash hook authoring:
|
|
148
|
+
|
|
149
|
+
| Principle | Bash hook discipline |
|
|
150
|
+
|---|---|
|
|
151
|
+
| **Economy of mechanism** | Hook body ≤200 lines for inspectability; helpers in `lib/*.sh` so the hook reads top-to-bottom |
|
|
152
|
+
| **Fail-safe defaults** | `set -euo pipefail` at top of every hook — undefined variables and pipe failures FAIL rather than passing silently |
|
|
153
|
+
| **Complete mediation** | PreToolUse hooks fire on every matching tool call, not "first call per session" |
|
|
154
|
+
| **Open design** | Hook trigger conditions documented in `docs/hooks-index.md`; BLOCKED banners name the path the hook reads |
|
|
155
|
+
| **Separation of privilege** | Destructive hooks (e.g., deploy-guard.sh) check operator confirmation AND state-spine signal AND env var — three independent conditions |
|
|
156
|
+
| **Least privilege** | Hooks `cd` to repo root only when needed; `git` commands scope to specific paths; file modes scoped to minimum-needed (644 for data, 755 for executables) |
|
|
157
|
+
| **Least common mechanism** | Per-hook test files (`.claude/hooks/tests/<name>.test.sh`); per-hook trace context; no shared global state between hooks |
|
|
158
|
+
| **Psychological acceptability** | `SKIP_<HOOK>=1` per-call override; logged via trace-helper; operator-bypass is per-call, surface-visible |
|
|
159
|
+
|
|
160
|
+
This translation block is the substrate-internal reference for Phase X-3 (`standards/bash-hook-safety.md` + `.claude/rules/defensive-bash.md`). The standard codifies the discipline; this luminary names the principles the discipline implements.
|
|
161
|
+
|
|
162
|
+
## When to cite this luminary
|
|
163
|
+
|
|
164
|
+
Cite `@luminary saltzer-schroeder` when:
|
|
165
|
+
|
|
166
|
+
- **Reviewing protection mechanisms** (`/security-review`, `/architect-review`) — does this default deny or permit? Is mediation complete? Is privilege minimal?
|
|
167
|
+
- **Designing default behaviors** — particularly when a default determines whether dangerous-by-default or safe-by-default applies (the auto-save case is the SENTINEL_bassclef_example)
|
|
168
|
+
- **Adding override paths** — does the override get logged? Is the trade-off named? Is the override scope per-call or per-session?
|
|
169
|
+
- **Authoring hooks** — bash hook safety (Phase X-3) instantiates the principles; the luminary names what the hook discipline is implementing
|
|
170
|
+
- **Diagnosing security regressions** — `/diagnose` reaches for this luminary when the failure surface involves trust boundaries, default behavior, scope creep, or missed checks
|
|
171
|
+
- **Designing tenant isolation** — least common mechanism + least privilege apply to multi-tenant systems directly
|
|
172
|
+
|
|
173
|
+
For threat-model authoring (analyzing what attackers can do given the protection mechanisms), route to Schneier — separate luminary candidate; Phase X+ follow-on.
|
|
174
|
+
INSTEAD of stretching Saltzer & Schroeder into threat modeling: cite Schneier when the catalog lands.
|
|
175
|
+
|
|
176
|
+
For cryptographic primitive selection — out of bassclef's anchor catalog today.
|
|
177
|
+
|
|
178
|
+
## Composes with
|
|
179
|
+
|
|
180
|
+
- **John Ousterhout** — Deep modules + define errors out of existence overlap with economy-of-mechanism + complete-mediation. Ousterhout's design judgment frames WHEN to apply Saltzer & Schroeder.
|
|
181
|
+
- **Kent Beck** — Tests for protection mechanisms verify the default behavior matches the design intent. A test that asserts "auto-save refuses main" is the mechanical guarantee fail-safe-defaults specifies.
|
|
182
|
+
- **Don Norman** — Psychological acceptability is the same surface as Norman's cognitive-load principle for system design. The operator-facing behavior of a security mechanism IS its UX.
|
|
183
|
+
INSTEAD of treating them as separate concerns: Norman's lens applies directly to override design + BLOCKED banner phrasing + hook-error message clarity.
|
|
184
|
+
- **Edward Tufte** — When protection mechanisms produce telemetry (e.g., trace logs of overrides), Tufte's information-design discipline anchors how that telemetry surfaces to operators.
|
|
185
|
+
|
|
186
|
+
## Refs
|
|
187
|
+
|
|
188
|
+
- bassclef#1058 — concrete failure (auto-save scope hole) that surfaced the need for this luminary at the substrate
|
|
189
|
+
- bassclef#1060 / PR #1061 — temperance mechanical block (sibling discipline-becomes-mechanical pattern; complete-mediation in action)
|
|
190
|
+
- `.claude/rules/destructive-operations.md` — agent self-assessment rule that this luminary anchors
|
|
191
|
+
- `.claude/rules/security.md` — existing rule scoped to `.ts/.tsx/.json/.env/Dockerfile`; bash extension is Phase X-3
|
|
192
|
+
- `docs/iteration-bets/2026-06-17a-secure-coding-foundations-longrun.md` Phase X scope
|
|
193
|
+
- `standards/code-safety-principles.md` (Phase X-2) — language-agnostic principles document this luminary anchors
|
|
194
|
+
- `standards/bash-hook-safety.md` (Phase X-3) — bash-specific instantiation of the stack-translation table above
|