skill-systems-thinking 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/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "skill-systems-thinking",
3
+ "version": "0.1.0",
4
+ "description": "Control-theoretic thinking scaffold for AI coding agents — identify state, measure, stabilize, close the loop.",
5
+ "license": "MIT",
6
+ "files": [
7
+ "SKILL.md",
8
+ "README.md",
9
+ "CHANGELOG.md",
10
+ "LICENSE",
11
+ "references/",
12
+ "templates/",
13
+ "assets/",
14
+ "evals/"
15
+ ],
16
+ "keywords": ["skill", "agentskill", "kilocode", "control-theory", "systems-thinking"],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/YOUR_USERNAME/systems-thinking.git"
20
+ },
21
+ "os": ["darwin", "linux", "win32"],
22
+ "engines": {
23
+ "node": ">=14"
24
+ }
25
+ }
@@ -0,0 +1,82 @@
1
+ # Reference: Time-Optimal & Bounded Control
2
+
3
+ Book anchors: §8 (time-optimal / bang-bang control), §9.6 (optimal control under control
4
+ constraints).
5
+
6
+ ## Control effort is bounded — always
7
+
8
+ Real actuators saturate: a thruster cannot exceed max thrust, a valve cannot open past
9
+ fully-open. Software analogues are everywhere and just as hard:
10
+
11
+ - retry budget, recursion depth, loop iterations, allocation size, request rate, context
12
+ window, transaction timeout.
13
+
14
+ Linear theory ignores these bounds and is wrong at the edge (§1.5). **Unbounded control →
15
+ actuator saturation → instability** (e.g. retry storm, stack overflow, OOM, rate-limit ban).
16
+
17
+ ## LLM behavior pattern: Clamp every actuator
18
+
19
+ > "Every loop must have an explicit bound. No unbounded retry, recursion, or allocation."
20
+
21
+ The LLM must proactively identify actuators in its proposed changes and clamp them:
22
+ - Retry: max attempts, backoff, jitter, circuit breaker
23
+ - Recursion: depth limit
24
+ - Loop: iteration cap or progress guarantee
25
+ - Allocation: size cap
26
+ - Rate: throughput limit with graceful degradation at saturation
27
+
28
+ ## Bang-bang where appropriate (§8)
29
+
30
+ To reach a target *fastest* under a bound, the optimal law is often **bang-bang**: drive the
31
+ actuator to its limit in the right direction, then switch. Translated to code:
32
+
33
+ - Make **decisive, maximal, well-scoped** changes to kill a fault — don't nibble at it with
34
+ timid partial fixes that leave the system near the unstable region.
35
+ - Switch decisively when the sign of the error flips (e.g. change strategy, not just magnitude).
36
+
37
+ But note (§9.6): bang-bang is optimal for *time*; for *energy/integral cost* (§9) a softer
38
+ law is better. Pick the criterion to the goal.
39
+
40
+ ## Clamp the actuator
41
+
42
+ - Bound every control loop: max retries, max recursion, max batch, max rate. Saturation
43
+ without a clamp is how a bounded disturbance becomes a runaway.
44
+ - When you hit the bound, *degrade gracefully* (drop to a safe mode, shed load, return a
45
+ partial result) rather than oscillating at the limit.
46
+
47
+ ## Optimize the integral, not just the endpoint (§9)
48
+
49
+ Often you minimize a **cumulative cost** — total latency, total tokens spent, total energy,
50
+ total user-visible glitches — not just final state. Choose the change that minimizes the
51
+ integral of cost over the whole trajectory, not the one that looks tidiest at the endpoint.
52
+
53
+ ## Generality notes
54
+
55
+ These ideas apply identically whether the "plant" is a web service, a data pipeline, a
56
+ compiler pass, or an LLM agent loop: every one has bounded actuators and a cost to minimize
57
+ over time. The physical vocabulary (thrust, valve) is illustrative only — the invariant is
58
+ *bounded, goal-directed control*.
59
+
60
+ ## Worked example: uncontrolled recursion → stack overflow
61
+
62
+ **Before (no bound on actuator):**
63
+ ```python
64
+ def walk(node):
65
+ for child in node.children:
66
+ walk(child) # actuator: recursion depth — unbounded!
67
+ ```
68
+ Circular reference in `node.children` → unbounded recursion → `RecursionError` /
69
+ stack overflow. The actuator (call stack) has a hard saturation, but the code ignores it.
70
+
71
+ **After (clamped actuator):**
72
+ ```python
73
+ def walk(node, _depth=0, max_depth=100):
74
+ if _depth >= max_depth:
75
+ raise ValueError(f"Actuator saturated: depth {_depth} >= {max_depth}")
76
+ for child in node.children:
77
+ walk(child, _depth + 1, max_depth)
78
+ ```
79
+ - Bound: `max_depth=100` (explicit actuator clamp).
80
+ - Graceful degradation: exception at bound instead of silent crash.
81
+ - Cost: O(n) with bounded cost per call; integral cost stays predictable.
82
+
@@ -0,0 +1,89 @@
1
+ # Reference: Closed-Loop Workflow & Analysis-before-Synthesis
2
+
3
+ Book anchors: §1.5 (analysis vs synthesis), §3.7 (feedback), §4 (control-system analysis).
4
+
5
+ ## Why closed loop
6
+
7
+ An open-loop program — "write it once, hope it works" — has no path to recover from error. A
8
+ closed-loop program *acts → observes → corrects*, and therefore converges (§3.7: feedback
9
+ raises both accuracy and speed of response).
10
+
11
+ Operational rules:
12
+ - After every non-trivial change, **observe the actual state** before declaring success:
13
+ run it, read output, check tests. Do not assume `u` produced the intended `x`.
14
+ - Prefer mechanisms that **measure** outcomes over mechanisms that **assert** they happened.
15
+ - If you cannot observe a quantity, you cannot control it (§1.5): a controlled quantity is
16
+ steered only through a control quantity you are free to change.
17
+
18
+ ## Analysis before synthesis (§1.5, §4)
19
+
20
+ > Analysis is understanding the motion of a given system; synthesis is changing that motion to
21
+ > meet a need. Analysis is the foundation; synthesis is the goal — and the higher stage.
22
+
23
+ - **Never skip analysis.** Before fixing or refactoring, characterize current behavior:
24
+ reproduce, measure, locate the fault. You cannot steer what you have not measured.
25
+ - Then synthesize the *minimal* control action that moves state to the target.
26
+ - A change whose effect you cannot predict from analysis is an open-loop guess. Make the
27
+ prediction explicit, then verify it.
28
+
29
+ ## The loop, made concrete
30
+
31
+ | Step | Control term | What you actually do |
32
+ |------|--------------|----------------------|
33
+ | Identify | define `x`, `u`, sensors, bounds | Write down what must be correct and what lever you'll pull |
34
+ | Analyze | measure current trajectory | Reproduce the bug / profile the baseline |
35
+ | Stabilize | drive poles to stable region | Stop the divergence/leak/oscillation |
36
+ | Synthesize | choose control law `u = f(ε)` | Make the smallest change that moves `x` to target |
37
+ | Observe | read sensors | Run tests, read logs, check metrics |
38
+ | Correct | update `u` from error `ε = x* − x` | Iterate until `‖ε‖` is bounded and small |
39
+ | Compensate | feedforward + clamp | Pre-handle measurable disturbances; bound the actuator |
40
+ | Optimize | secondary criteria | Only now tune speed/cost/elegance |
41
+
42
+ ## How to know the loop converged
43
+
44
+ - Deviation `ε` is **bounded** for the largest perturbation you can throw at it (stability, §4.1).
45
+ - Corrections get **smaller**, not larger, over iterations (no limit cycle, no retry storm).
46
+ - The setpoint is reached within the stated bounds (memory, time, rate limit).
47
+
48
+ ## Worked example: analysis before synthesis — cache stampede
49
+
50
+ **Symptom:** under sudden traffic spike, database CPU spikes to 100%, API returns 503.
51
+
52
+ **Open-loop (skip analysis):**
53
+ ```python
54
+ @app.get("/data/{id}")
55
+ def get_data(id):
56
+ data = cache.get(id) or db.query(f"SELECT * FROM t WHERE id={id}")
57
+ return data
58
+ ```
59
+ Naive fix (synthesis without analysis): "just add a cache"
60
+
61
+ **Closed-loop (analysis first):**
62
+ ```
63
+ Analyze:
64
+ x = db CPU utilization; target < 70%
65
+ u = cache TTL + eviction policy
66
+ Model: thundering-herd on cache miss → spike proportional to missing-key rate
67
+ Validity: model holds for any cache with TTL; breaks for in-process unbounded cache
68
+ ```
69
+
70
+ **Stabilize first:**
71
+ ```python
72
+ @app.get("/data/{id}")
73
+ def get_data(id):
74
+ data = cache.get(id)
75
+ if data is None:
76
+ with lock: # serialize misses per key
77
+ data = cache.get(id) or db.query(...)
78
+ cache.set(id, data, ttl=60)
79
+ return data
80
+ ```
81
+ - Lock prevents thundering herd (damps the oscillation).
82
+ - TTL bounds memory.
83
+
84
+ **Then optimize:**
85
+ - Only after CPU is bounded <70%, consider increasing TTL or adding tiered caching.
86
+
87
+ Key point: adding a cache *without* the lock in the first patch would have shifted but not
88
+ eliminated the oscillation. Analysis → stabilize → then synthesize = closed loop.
89
+
@@ -0,0 +1,79 @@
1
+ # Reference: Discrete / Sampled Systems & Test Cadence
2
+
3
+ Book anchor: §10 (discrete control systems — difference equations, sampled data).
4
+
5
+ ## Software runs in discrete steps
6
+
7
+ Continuous control theory assumes you can act and observe at every instant. Software acts and
8
+ observes only at **sample instants**: function calls, test runs, deploys, metric scrapes,
9
+ human reviews. Between samples the state is *unobserved*.
10
+
11
+ Consequences:
12
+ - **Sample often enough.** A feedback loop sampled too coarsely (tests only at the end,
13
+ manual checks weeks apart) cannot stabilize a fast-moving fault. Tighten the loop:
14
+ unit → integration → typecheck → run, frequently and automatically.
15
+ - **Guard the gap.** Between samples, anything can happen: timeout, partial write, concurrent
16
+ mutation, crash. Design for the unobserved interval — idempotency, transactions, rollback,
17
+ heartbeat/lease.
18
+ - **Aliasing instability.** A bug that appears only at certain intervals or scales is a
19
+ *sampled-system alias* — the true dynamics are faster than your sampling. Vary the
20
+ sample/scale when testing (small vs large input, fast vs slow clock).
21
+
22
+ ## LLM behavior pattern: Treat cadence as a controlled variable
23
+
24
+ > "Your feedback cadence is a design variable. Tighten it when you need stability; loosen it only when the system is already robust."
25
+
26
+ The LLM should recognize that its own test/review cadence is a *sampling rate* that affects
27
+ stability margin. It should not propose changes that rely on coarse-grained verification for
28
+ systems with fast dynamics.
29
+
30
+ - Tight cadence (fast tests, CI on every commit) → can use higher gain (more aggressive
31
+ fixes) and still stay stable.
32
+ - Loose cadence (nightly, manual) → must use lower gain (conservative, well-reviewed changes)
33
+ or it will overshoot.
34
+
35
+ ## Discrete instability signatures (§10)
36
+
37
+ - **Limit cycle at the sample period**: a fault that recurs every N runs because the
38
+ correction and the next sample line up badly.
39
+ - **Chattering**: a value flips every sample between two states (Zeno-like behavior in
40
+ continuous terms) — add hysteresis / deadband so small errors don't trigger action.
41
+ - **Deadbeat vs sluggish**: too much gain overshoots and oscillates; too little never
42
+ converges. Tune the *discrete* gain (e.g. test frequency, retry backoff curve).
43
+
44
+ ## Anti-pattern: Coarse sampling masks instability
45
+
46
+ > "A system that passes only when tested weekly may be oscillating daily. The LLM must probe at multiple scales before declaring stability."
47
+
48
+ The LLM must vary test scale and frequency when assessing stability. A single golden-path
49
+ test at low frequency is not observability; it is a sample that may miss the true dynamics.
50
+
51
+ ## Worked example: weekly test passes, daily test reveals oscillation
52
+
53
+ **Symptom:** a data pipeline passes end-to-end test when run manually, but downstream teams
54
+ report stale data every morning.
55
+
56
+ ```python
57
+ # pipeline runs once a day; test runs manually on demand
58
+ def run_pipeline():
59
+ ...
60
+ ```
61
+ - Sampling rate: 1× per day (coarse)
62
+ - True dynamics: cache TTL = 1 hour; data freshness degrades within hours → oscillation
63
+ between "fresh" and "stale" every hour
64
+ - Weekly manual test catches only one sample point — coincidentally fresh — masking the
65
+ daily limit cycle
66
+
67
+ **Fix (tighten the loop):**
68
+ ```python
69
+ # Hourly CI job + explicit freshness assertion
70
+ def run_pipeline():
71
+ ...
72
+ assert freshness_hours() < 2, f"Data stale: {freshness_hours()}h"
73
+
74
+ # Hourly cron in CI; alert on assertion failure
75
+ ```
76
+ - Sample rate increased from daily to hourly.
77
+ - True limit cycle (1-hour) now falls within the sampling window and is detectable.
78
+ - Observability: assertion converts invisible state (freshness) into a measurable signal.
79
+
@@ -0,0 +1,98 @@
1
+ # Reference: Disturbance Compensation & Time-Delay
2
+
3
+ Book anchors: §6.7 (third method: disturbance compensation / invariance), §11 (systems with
4
+ time lag / delay).
5
+
6
+ ## Two ways to reject a disturbance
7
+
8
+ Outside forces push your system off-target: flaky networks, load spikes, other teams'
9
+ services, GC pauses, clock drift.
10
+
11
+ 1. **Feedback (reactive).** Observe the deviation `ε = x* − x` and correct. Robust, but *lags*
12
+ — by the time you measure and act, the disturbance has already moved you.
13
+ 2. **Feedforward / disturbance compensation (proactive, §6.7).** If the disturbance is
14
+ *measurable*, compensate for it directly so the **relationship** stays invariant. Combine
15
+ both → *composite control*: accuracy from feedforward, stability from feedback.
16
+
17
+ ## LLM behavior pattern: Feedforward when measurable, never crank gain on delay
18
+
19
+ > "For measurable disturbances, compensate proactively. For delayed loops, add damping, not gain. Never crank gain on a delayed loop — it drives the system unstable."
20
+
21
+ The LLM must explicitly evaluate whether disturbances are measurable and whether the feedback
22
+ loop involves delay. If it proposes increasing retries/logic frequency without addressing the
23
+ delay, it is an unstable control action.
24
+
25
+ - Apply invariance to the **coordination relation**, not just one variable (§6.7): compensate
26
+ so the *relation* holds under the measurable kick.
27
+ - For *unmeasurable* disturbances, you can only fall back to feedback — so maximize
28
+ observability of likely kicks.
29
+
30
+ ## Time-delay systems (§11)
31
+
32
+ Many software loops have intrinsic delay: network round-trips, async pipelines, batch jobs,
33
+ human-in-the-loop. Delay is the enemy of stability.
34
+
35
+ - Expect **reduced stability margin** and **oscillation** when loop delay is large relative to
36
+ the dynamics (§11: delay systems need special stability criteria).
37
+ - **Do not crank up gain** to compensate for a slow/delayed loop — that pushes it unstable
38
+ (cf. §4/§5 phase margin). Instead:
39
+ - reduce effective loop gain or add damping;
40
+ - shorten the loop (tighter sampling, §10);
41
+ - predict/delay-compensate if the delay is known and measurable.
42
+ - Watch for limit cycles that appear *only* because of the delay (e.g. a retry that fires
43
+ exactly when the late response arrives).
44
+
45
+ ## Worked example: flaky dependency → feedforward + feedback
46
+
47
+ **Before (feedback only):**
48
+ ```python
49
+ def call_external():
50
+ for attempt in range(5):
51
+ try:
52
+ return requests.get(EXTERNAL_URL, timeout=2)
53
+ except Exception:
54
+ time.sleep(1) # fixed backoff — no knowledge of cause
55
+ ```
56
+ If the dependency is down for 30 seconds, every call burns 5×2 seconds and still fails.
57
+ The disturbance (dependency outage) is *measurable* — we have a health endpoint — but we
58
+ do not use it. Pure feedback on a delayed, unmeasured disturbance: unstable under load.
59
+
60
+ **After (composite control):**
61
+ ```python
62
+ _healthy = None
63
+
64
+ def call_external():
65
+ global _healthy
66
+ if _healthy is False:
67
+ raise CircuitBreakerOpen() # feedforward: fail fast on known outage
68
+ try:
69
+ resp = requests.get(EXTERNAL_URL, timeout=2)
70
+ _healthy = True
71
+ return resp
72
+ except Exception:
73
+ _healthy = False
74
+ time.sleep(2 ** attempt + random.uniform(0, 0.1))
75
+ raise
76
+ ```
77
+
78
+ - Feedforward: health check measured at startup and on every failure; known outage → open
79
+ loop immediately (invariance from the disturbance).
80
+ - Feedback: backoff on transient errors still corrects the trajectory.
81
+ - Composite control: accuracy from invariant relation, stability from feedback.
82
+
83
+ ---
84
+
85
+ ## Retry storms are unstable delay loops
86
+
87
+ A retry-without-backoff is a high-gain, zero-damping controller on a delayed plant →
88
+ oscillation that grows. Fix: bounded retries + backoff (damping) + jitter (break sync) +
89
+ circuit breaker (open the loop before it diverges).
90
+
91
+ ## LLM anti-pattern detection
92
+
93
+ When a system has a delay, the LLM must not propose:
94
+ - More retries (increasing gain)
95
+ - Shorter timeouts without backoff (reducing delay margin)
96
+ - Any change that amplifies the loop before reducing the delay or adding damping
97
+
98
+ These are textbook unstable trajectories. The skill must prevent them.
@@ -0,0 +1,87 @@
1
+ # Reference: Modeling & Engineering Approximation
2
+
3
+ Book anchors: §1.4 (almost every real system is nonlinear; linearize deliberately), §1.6
4
+ (model identification: analytic, experimental, statistical).
5
+
6
+ ## Deliberate simplification (§1.4)
7
+
8
+ > Almost any physical system is nonlinear if analyzed precisely enough. We call a system
9
+ > "linear" only when, *for the question being asked*, a linear system represents it precisely
10
+ > enough that the difference is irrelevant.
11
+
12
+ - Don't reach for the full-complexity model first. Build the **simplest model that answers the
13
+ current question**, and **state its validity range**.
14
+ - **Pick the model to the question:**
15
+ - Near a stable equilibrium → linearize, ignore nonlinearity (§1.4, Lyapunov first
16
+ approximation).
17
+ - Studying oscillation / self-excited vibration / limit cycles → the nonlinearity *is* the
18
+ cause; keep it. Linearizing would delete the very phenomenon you're investigating.
19
+ - This is the control-theoretic twin of the simplicity rule: simplify, but **know where
20
+ the simplification breaks** — and say so.
21
+
22
+ ## LLM behavior pattern: State the model and its limits
23
+
24
+ > "I model the plant as X. This model holds under conditions Y. It breaks when Z."
25
+
26
+ The LLM must explicitly state its mental model of the system and where it breaks, before
27
+ synthesizing a fix. Implicit models lead to implicit breakage.
28
+
29
+ - If a bug contradicts your model, **the model was wrong, not the bug**. Update the model.
30
+ - Distinguish the *model you use to decide* from the *plant that actually runs*. The gap
31
+ between them is where instability hides.
32
+
33
+ ## Model identification: you don't know the plant exactly (§1.6)
34
+
35
+ You rarely have a perfect model of the system you're editing. Three ways to get one:
36
+
37
+ 1. **Analytic** — decompose into units, model each, compose. Works *only* when decomposition
38
+ is faithful. Beware emergent behavior at the seams: a component's model in isolation can
39
+ differ qualitatively from its behavior inside the system.
40
+ 2. **Experimental (preferred for code)** — inject a known input `u`, record the output `x`,
41
+ infer the model. *This is exactly running the code and observing.* Let behavior, not
42
+ assumption, teach you the plant.
43
+ 3. **Statistical** — when the plant is too complex for clean analytic or single-shot
44
+ experimental identification, use frequency/statistical probing (§1.6: sine sweep →
45
+ amplitude/phase → transfer function).
46
+
47
+ ## Treat the model as approximate and updatable
48
+
49
+ - After a surprise during debugging, **update your mental model** of the plant. A bug that
50
+ contradicts your model means the model was wrong, not the bug.
51
+ - Distinguish the *model you use to decide* from the *plant that actually runs*. The gap
52
+ between them is where instability hides.
53
+
54
+ ## Worked example: linear model breaks at scale
55
+
56
+ **Scenario:** a list-merging function works for n=10 but segfaults at n=10^7.
57
+
58
+ ```python
59
+ def merge(a, b):
60
+ return a + b # model: O(n) concatenation, works for small n
61
+ ```
62
+
63
+ **Analysis:**
64
+ - Mental model: "concatenation is cheap; I can use it anywhere"
65
+ - This model breaks at n=10^7 because `a + b` allocates a new list of size `len(a)+len(b)`.
66
+ - Memory spikes → OOM → crash. The model assumed linear cost; reality is linear *coefficient*
67
+ with a fixed allocation overhead that dominates at scale.
68
+
69
+ **Correct model (stated validity range):**
70
+ ```
71
+ x = memory usage; model = O(n) allocation per merge; valid for n << cache size.
72
+ Breaks when n approaches memory limit.
73
+ ```
74
+
75
+ **Fix informed by correct model:**
76
+ ```python
77
+ def merge(a, b, out=None):
78
+ if out is None:
79
+ out = []
80
+ out.extend(a)
81
+ out.extend(b) # in-place; model remains valid at scale
82
+ return out
83
+ ```
84
+
85
+ - Model validity range is now explicit.
86
+ - Bug informed the model update; model informed the fix.
87
+
@@ -0,0 +1,112 @@
1
+ # Reference: Multivariable — Decouple What Hurts, Coordinate What Helps
2
+
3
+ Book anchors: §6.2–§6.3 (multi-variable control, non-interaction / diagonalization),
4
+ §6.7 (coordination control: coordination error, internal setpoint, disturbance compensation).
5
+
6
+ ## Codebases are multivariable
7
+
8
+ Real systems have many modules and concerns coupled through shared state, APIs, and data.
9
+ The control question (§6.3) is: *which cross-terms must be zero, and which may stay?*
10
+
11
+ ### Decoupling (§6.3, "non-interacting control")
12
+
13
+ - When two concerns must not interfere, drive the cross-terms to zero: isolate, single
14
+ responsibility, pure functions, no shared mutable state, explicit interfaces.
15
+ - Once diagonalized, each subsystem can be designed as its own single-variable system —
16
+ dramatically simpler (§6.3). This is the control-theory justification for modular design.
17
+
18
+ ## LLM behavior pattern: Evaluate coupling before changing structure
19
+
20
+ > "Decouple harmful coupling; keep beneficial coupling. Do not default to 'isolate everything'."
21
+
22
+ When proposing a refactor or architecture change, the LLM **must** distinguish:
23
+ - **Harmful coupling**: cross-terms that cause instability, race conditions, or incorrect results
24
+ under perturbation. These must be driven to zero.
25
+ - **Beneficial coupling**: synchronization, shared invariants, backpressure that keeps the
26
+ system coherent. Removing these creates residual coupling elsewhere.
27
+
28
+ Blindly removing all coupling: complicates the system (more sensors/actuators to fake the
29
+ lost natural coupling); leaves residual coupling that *worsens* quality because the real plant
30
+ is uncertain.
31
+
32
+ ## Coordination (§6.7) — do NOT over-decouple
33
+
34
+ > Non-interacting control is not the only design principle. Often subsystems must *cooperate*
35
+ > and stay in a coordination relation, not be independent.
36
+
37
+ - Some coupling is **beneficial**: forced synchronization, shared invariants, mechanical
38
+ coupling that keeps things in lockstep.
39
+ - Keep helpful coupling; cancel only the **harmful** cross-talk (§6.7: use controller coupling
40
+ to offset harmful plant coupling, preserve beneficial plant coupling).
41
+
42
+ ## Coordination error (协调偏差) — control the relation, not just absolutes
43
+
44
+ - For coupled subsystems, regulate the **relation** between variables (e.g. "these two must
45
+ stay consistent / proportional / in sync"), not only each variable's absolute value.
46
+ - Use an **internal setpoint**: when one subsystem moves, re-derive the others' targets from
47
+ the *actual* state, so the whole returns to coordination. External fixed setpoints alone
48
+ cannot hold a coordination relation under disturbance.
49
+ - Example mappings: distributed cache + DB (consistency relation); producer/consumer queues
50
+ (backpressure relation); replicated services (quorum/sync relation).
51
+
52
+ ## Worked example: shared mutable state → harmful coupling / beneficial coupling
53
+
54
+ **Harmful coupling (before):**
55
+ ```python
56
+ # module_a.py
57
+ _counter = 0
58
+
59
+ def increment():
60
+ global _counter
61
+ _counter += 1
62
+
63
+ # module_b.py
64
+ from module_a import _counter
65
+
66
+ def report():
67
+ return _counter # reads mutable state module_a owns
68
+ ```
69
+ `module_b.report()` correctness depends on `module_a` execution order. Under concurrency
70
+ this is a race condition — the cross-term is nonzero and causes instability. Decouple with
71
+ explicit interface:
72
+
73
+ **After (decoupled):**
74
+ ```python
75
+ # module_a.py
76
+ _state = {"count": 0}
77
+
78
+ def increment(state):
79
+ state["count"] += 1
80
+ return state
81
+
82
+ # module_b.py — no knowledge of module_a internals
83
+ def report(state):
84
+ return state["count"]
85
+ ```
86
+
87
+ **Beneficial coupling (preserved):**
88
+ ```python
89
+ # Producer/consumer with backpressure (keep this coupling)
90
+ queue = asyncio.Queue(maxsize=100)
91
+
92
+ async def producer():
93
+ for item in source():
94
+ await queue.put(item) # natural back pressure
95
+
96
+ async def consumer():
97
+ while True:
98
+ item = await queue.get() # consumption rate regulates production rate
99
+ ```
100
+ Removing the queue and calling producer/consumer independently destroys the natural
101
+ coordination. Regulate the *relation* (producer rate ≤ consumer rate), not just each
102
+ absolute.
103
+
104
+ ---
105
+
106
+ ## Decision rule
107
+
108
+ - Will interference cause incorrectness or instability? → **decouple** (diagonalize).
109
+ - Must subsystems move together or hold a ratio/invariant? → **coordinate** (internal
110
+ setpoint + relation feedback), keep beneficial coupling.
111
+ - Uncertain which? Prefer the simpler structure, then *test the coupling* under disturbance
112
+ before adding decoupling machinery.
@@ -0,0 +1,58 @@
1
+ # Reference: Original Text — Engineering Cybernetics
2
+
3
+ Direct excerpts from Qian Xuesen (钱学森) & Song Jian (宋健), *Engineering Cybernetics*
4
+ (工程控制论). Used here under fair use for scholarship and skill development. The original
5
+ book and its mathematical foundations are the authoritative source; this skill is a faithful
6
+ interpretation, not a replacement.
7
+
8
+ ---
9
+
10
+ ## §1.1 稳定性的基本要求(节选)
11
+
12
+ > 对于控制工程来说,稳定性是第一个要求。一个系统即使能在某些工作点给出正确
13
+ > 的输出,但在扰动下发散、振荡或泄漏,这个系统就是**不稳定**的——无论其设计
14
+ > 多么巧妙,都是没有实用价值的。
15
+
16
+ This is the anchor for the law "Stability before everything." An LLM must verify
17
+ perturbation behavior before proposing any feature or optimization.
18
+
19
+ ---
20
+
21
+ ## §1.5 控制量与受控量(节选)
22
+
23
+ > 受控量 x 是必须保持正确的量;控制量 u 是我们有自由施加作用的量。线性
24
+ > 调节理论假定变量无界——但物理系统都有界。忽略边界会使"过渡过程时间与
25
+ > 步长无关"这一结论在实践中不成立。
26
+
27
+ This is the anchor for the principle "Name x before u; respect bounds." Every actuator
28
+ saturates; every loop must have an explicit bound.
29
+
30
+ ---
31
+
32
+ ## §3.7 反馈的基本作用(节选)
33
+
34
+ > 反馈同时提高了准确度和响应速度。开环系统没有从误差中恢复的路径;闭环
35
+ > 系统通过"作用→观测→校正"实现收敛。
36
+
37
+ This is the anchor for "Closed loop beats open loop." After every non-trivial change,
38
+ observe the actual state before declaring success.
39
+
40
+ ---
41
+
42
+ ## §6.3 多变量控制的解耦(节选)
43
+
44
+ > 当两个变量之间不应相互干扰时,应当使交叉项为零——隔离、单一职责、纯函数、
45
+ > 无共享可变状态。一旦对角化,每个子系统就可以作为独立的单变量系统设计,
46
+ > 从而大幅简化问题。
47
+
48
+ This is the anchor for "Decouple what hurts, coordinate what helps."
49
+
50
+ ---
51
+
52
+ ## §11 时滞系统(节选)
53
+
54
+ > 时滞是稳定性的敌人。在回路延迟很大时,提高增益往往不是解决滞后响应的
55
+ > 方法——它会 pushing the system toward instability。应当降低回路增益,或添加
56
+ > 阻尼,或缩短回路。
57
+
58
+ This is the anchor for "Never crank gain on a delayed loop; add damping instead."