diff --git a/harness/pstack/claude-code/README.md b/harness/pstack/claude-code/README.md index 723a594e2..7f9ce7a98 100644 --- a/harness/pstack/claude-code/README.md +++ b/harness/pstack/claude-code/README.md @@ -217,10 +217,10 @@ pstack also ships [Comment Sicko](./agents/comment-sicko.md), a read-only commen ## principles -twenty-four short skills, one principle each. `poteto-mode` indexes them inline and reads that index at task start. the standalone files are there so other skills can reference a principle by name, and so the index can point at the full rule for each. +twenty-five short skills, one principle each. `poteto-mode` indexes them inline and reads that index at task start. the standalone files are there so other skills can reference a principle by name, and so the index can point at the full rule for each.
-all twenty-four principles +all twenty-five principles | principle | group | rule | |---|---|---| @@ -238,6 +238,7 @@ twenty-four short skills, one principle each. `poteto-mode` indexes them inline | [boundary-discipline](./skills/principle-boundary-discipline/SKILL.md) | architecture | Concentrate guards at system boundaries (CLI, config, network, external APIs); trust internal types and keep business logic in pure functions. | | [type-system-discipline](./skills/principle-type-system-discipline/SKILL.md) | architecture | Make illegal states unrepresentable, brand semantic primitives, parse external data at boundaries, exhaust variants, derive from authoritative schemas. | | [make-operations-idempotent](./skills/principle-make-operations-idempotent/SKILL.md) | architecture | Converge to the same end state regardless of partial prior runs. | +| [bound-every-external-call](./skills/principle-bound-every-external-call/SKILL.md) | architecture | Give every external call a deadline, a safe retry policy, a dead-letter path, and a circuit breaker. | | [migrate-callers-then-delete-legacy-apis](./skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md) | architecture | Migrate callers and delete the old API in the same wave instead of preserving compatibility layers. | | [separate-before-serializing-shared-state](./skills/principle-separate-before-serializing-shared-state/SKILL.md) | architecture | Eliminate the sharing first; serialize structurally only when one shared writer is a real invariant. | | [prove-it-works](./skills/principle-prove-it-works/SKILL.md) | verification | Apply after completing a task, before declaring done. Verify against the real artifact (run the feature, read the actual value, inspect the diff), not a proxy, self-report, or 'it compiles.'. | diff --git a/harness/pstack/claude-code/docs/guide/08-principles.md b/harness/pstack/claude-code/docs/guide/08-principles.md index d2921500b..a5d48cfbb 100644 --- a/harness/pstack/claude-code/docs/guide/08-principles.md +++ b/harness/pstack/claude-code/docs/guide/08-principles.md @@ -1,6 +1,6 @@ # Steer with principle names -pstack ships 24 principles as individual skills. `/poteto-mode` reads their index at the start of every multi-step task, applies the ones the task triggers, and names each applied principle in its reply along with the decision it changed. +pstack ships 25 principles as individual skills. `/poteto-mode` reads their index at the start of every multi-step task, applies the ones the task triggers, and names each applied principle in its reply along with the decision it changed. You don't invoke principles. You use their names to steer. Each name points at a complete rule the agent has already read, so one phrase redirects the work more precisely than a paragraph of instructions. @@ -26,7 +26,7 @@ separate before serializing shared state. give each attempt its own worktree, no Each phrase lands because the rule behind it is specific. The agent still has to say, in its reply, which decision the rule changed. A principle citation with no decision behind it means the agent named the rule without applying it. -## The 24, briefly +## The 25, briefly The core principles decide how much to build and when to rethink the design: @@ -47,6 +47,7 @@ The architecture principles decide where state, validation, and compatibility li - [Boundary Discipline](../../skills/principle-boundary-discipline/SKILL.md) validates at the boundary and trusts internal types. - [Type System Discipline](../../skills/principle-type-system-discipline/SKILL.md) makes illegal states unrepresentable. - [Make Operations Idempotent](../../skills/principle-make-operations-idempotent/SKILL.md) converges retries on the same end state. +- [Bound Every External Call](../../skills/principle-bound-every-external-call/SKILL.md) gives each call to a dependency a deadline, a safe retry policy, a dead-letter path, and a circuit breaker. - [Migrate Callers Then Delete Legacy APIs](../../skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md) migrates and deletes in one wave. - [Separate Before Serializing Shared State](../../skills/principle-separate-before-serializing-shared-state/SKILL.md) removes the sharing before adding coordination. diff --git a/harness/pstack/claude-code/docs/guide/README.md b/harness/pstack/claude-code/docs/guide/README.md index 17422b992..9da515221 100644 --- a/harness/pstack/claude-code/docs/guide/README.md +++ b/harness/pstack/claude-code/docs/guide/README.md @@ -11,7 +11,7 @@ Here's what you'll learn: 5. [Build and clean the change](./05-build-and-clean.md). The build playbooks, `/tdd`, `/unslop`, and `/no-comments`. 6. [Verify and ship](./06-verify-and-ship.md). Prove behavior on the real app, formally verify what tests rarely reach, vet numbers with `/benchmark-checklist`, then open a focused PR and drive it to merged. 7. [Run work while you sleep](./07-overnight.md). Trust before loops, an overnight contract, a decision log you can audit, and Projects and automations that scale past one agent. -8. [Steer with principle names](./08-principles.md). The 24 names that redirect an agent mid-task. +8. [Steer with principle names](./08-principles.md). The 25 names that redirect an agent mid-task. 9. [Make it yours](./09-make-it-yours.md). Your own mode, `/correct` for repeated mistakes, and how to test a skill change. 10. [Recipes and pitfalls](./10-recipes-and-pitfalls.md). Prompts to copy and mistakes to skip. diff --git a/harness/pstack/claude-code/skills/poteto-mode/SKILL.md b/harness/pstack/claude-code/skills/poteto-mode/SKILL.md index 1bb3884a5..3ef4c2cb8 100644 --- a/harness/pstack/claude-code/skills/poteto-mode/SKILL.md +++ b/harness/pstack/claude-code/skills/poteto-mode/SKILL.md @@ -60,6 +60,7 @@ Read the leaf skill in full for any principle you apply. Each entry names when i - **Boundary Discipline.** Wiring validation, error handling, or framework adapters. Guards at system boundaries, trust internal types, keep business logic pure. - **Type System Discipline.** Designing types or a signature in any typed language. Make illegal states unrepresentable, brand primitives, parse external data at boundaries. - **Make Operations Idempotent.** Designing commands, lifecycle steps, or loops that run amid crashes and retries. Converge to the same end state. +- **Bound Every External Call.** Calling a service, database, queue, third-party API, or subprocess you do not control. Set a deadline, retry only safe and transient failures with backoff, dead-letter poison messages, and break the circuit on a failing dependency. - **Migrate Callers Then Delete Legacy APIs.** Introducing a new internal API while old callers exist. Migrate and delete in one wave. - **Separate Before Serializing Shared State.** Concurrent actors might write the same file, branch, key, or object. Eliminate the sharing first. diff --git a/harness/pstack/claude-code/skills/principle-bound-every-external-call/SKILL.md b/harness/pstack/claude-code/skills/principle-bound-every-external-call/SKILL.md new file mode 100644 index 000000000..2f0b5b6cb --- /dev/null +++ b/harness/pstack/claude-code/skills/principle-bound-every-external-call/SKILL.md @@ -0,0 +1,28 @@ +--- +name: principle-bound-every-external-call +description: "Apply when code calls something it does not control: a network service, a database, a queue, a third-party API, or a subprocess. Give every call a deadline, retry only what is safe to retry, park messages that keep failing, and stop calling a dependency that is down." +disable-model-invocation: true +--- + +# Bound Every External Call + +Every call to a dependency you do not control can hang, fail, or succeed slowly. Decide what the code does in each case before you write the call, and make each answer a bound the code enforces. + +**Why:** An unbounded call turns one slow dependency into an outage everywhere it is called. A call with no deadline holds a thread or a connection until something else times out. A naive retry multiplies the load on a dependency that is already failing. A message that always fails blocks the queue behind it or disappears. + +**The idea:** give each external call four bounds. + +1. **A deadline.** Set an explicit timeout on every call, shorter than the budget of whoever waits on it. A library default is not a decision. Pass the remaining budget down so an inner call cannot outlive its caller. +2. **A retry policy.** Retry only errors that can be transient, such as timeouts, throttling, and 5xx responses, and only operations that are safe to repeat (see **make-operations-idempotent**). Use exponential backoff with jitter and a cap on attempts. Retry at one layer only. Retries stacked at several layers multiply into a retry storm. +3. **A place for poison messages.** In async work, move a message that keeps failing to a dead-letter queue after the attempt cap, with enough context to replay it. Do not drop it, and do not let it block the messages behind it. +4. **A circuit breaker.** When a dependency fails repeatedly, stop calling it for a while and fail fast or degrade on purpose. Probe it before you send full traffic again. + +Make each bound visible where the call is made, as named configuration or a typed client, not as library defaults that a reader cannot see. + +**The test:** +1. What happens if the dependency never answers? +2. What happens if it fails for ten minutes, then recovers? +3. Is every retried operation safe to run twice? +4. Where does a message go after its last attempt, and how is it replayed? + +Prove each answer with a test that uses a fake dependency that hangs, fails, and recovers, not with a comment. If an answer is "it waits" or "it retries forever," the call is missing a bound. diff --git a/pstack/README.md b/pstack/README.md index 40026a543..6ba1c3fb2 100644 --- a/pstack/README.md +++ b/pstack/README.md @@ -217,10 +217,10 @@ pstack also ships [Comment Sicko](./agents/comment-sicko.md), a read-only commen ## principles -twenty-four short skills, one principle each. `poteto-mode` indexes them inline and reads that index at task start. the standalone files are there so other skills can reference a principle by name, and so the index can point at the full rule for each. +twenty-five short skills, one principle each. `poteto-mode` indexes them inline and reads that index at task start. the standalone files are there so other skills can reference a principle by name, and so the index can point at the full rule for each.
-all twenty-four principles +all twenty-five principles | principle | group | rule | |---|---|---| @@ -238,6 +238,7 @@ twenty-four short skills, one principle each. `poteto-mode` indexes them inline | [boundary-discipline](./skills/principle-boundary-discipline/SKILL.md) | architecture | Concentrate guards at system boundaries (CLI, config, network, external APIs); trust internal types and keep business logic in pure functions. | | [type-system-discipline](./skills/principle-type-system-discipline/SKILL.md) | architecture | Make illegal states unrepresentable, brand semantic primitives, parse external data at boundaries, exhaust variants, derive from authoritative schemas. | | [make-operations-idempotent](./skills/principle-make-operations-idempotent/SKILL.md) | architecture | Converge to the same end state regardless of partial prior runs. | +| [bound-every-external-call](./skills/principle-bound-every-external-call/SKILL.md) | architecture | Give every external call a deadline, a safe retry policy, a dead-letter path, and a circuit breaker. | | [migrate-callers-then-delete-legacy-apis](./skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md) | architecture | Migrate callers and delete the old API in the same wave instead of preserving compatibility layers. | | [separate-before-serializing-shared-state](./skills/principle-separate-before-serializing-shared-state/SKILL.md) | architecture | Eliminate the sharing first; serialize structurally only when one shared writer is a real invariant. | | [prove-it-works](./skills/principle-prove-it-works/SKILL.md) | verification | Apply after completing a task, before declaring done. Verify against the real artifact (run the feature, read the actual value, inspect the diff), not a proxy, self-report, or 'it compiles.'. | diff --git a/pstack/docs/guide/08-principles.md b/pstack/docs/guide/08-principles.md index d2921500b..a5d48cfbb 100644 --- a/pstack/docs/guide/08-principles.md +++ b/pstack/docs/guide/08-principles.md @@ -1,6 +1,6 @@ # Steer with principle names -pstack ships 24 principles as individual skills. `/poteto-mode` reads their index at the start of every multi-step task, applies the ones the task triggers, and names each applied principle in its reply along with the decision it changed. +pstack ships 25 principles as individual skills. `/poteto-mode` reads their index at the start of every multi-step task, applies the ones the task triggers, and names each applied principle in its reply along with the decision it changed. You don't invoke principles. You use their names to steer. Each name points at a complete rule the agent has already read, so one phrase redirects the work more precisely than a paragraph of instructions. @@ -26,7 +26,7 @@ separate before serializing shared state. give each attempt its own worktree, no Each phrase lands because the rule behind it is specific. The agent still has to say, in its reply, which decision the rule changed. A principle citation with no decision behind it means the agent named the rule without applying it. -## The 24, briefly +## The 25, briefly The core principles decide how much to build and when to rethink the design: @@ -47,6 +47,7 @@ The architecture principles decide where state, validation, and compatibility li - [Boundary Discipline](../../skills/principle-boundary-discipline/SKILL.md) validates at the boundary and trusts internal types. - [Type System Discipline](../../skills/principle-type-system-discipline/SKILL.md) makes illegal states unrepresentable. - [Make Operations Idempotent](../../skills/principle-make-operations-idempotent/SKILL.md) converges retries on the same end state. +- [Bound Every External Call](../../skills/principle-bound-every-external-call/SKILL.md) gives each call to a dependency a deadline, a safe retry policy, a dead-letter path, and a circuit breaker. - [Migrate Callers Then Delete Legacy APIs](../../skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md) migrates and deletes in one wave. - [Separate Before Serializing Shared State](../../skills/principle-separate-before-serializing-shared-state/SKILL.md) removes the sharing before adding coordination. diff --git a/pstack/docs/guide/README.md b/pstack/docs/guide/README.md index 17422b992..9da515221 100644 --- a/pstack/docs/guide/README.md +++ b/pstack/docs/guide/README.md @@ -11,7 +11,7 @@ Here's what you'll learn: 5. [Build and clean the change](./05-build-and-clean.md). The build playbooks, `/tdd`, `/unslop`, and `/no-comments`. 6. [Verify and ship](./06-verify-and-ship.md). Prove behavior on the real app, formally verify what tests rarely reach, vet numbers with `/benchmark-checklist`, then open a focused PR and drive it to merged. 7. [Run work while you sleep](./07-overnight.md). Trust before loops, an overnight contract, a decision log you can audit, and Projects and automations that scale past one agent. -8. [Steer with principle names](./08-principles.md). The 24 names that redirect an agent mid-task. +8. [Steer with principle names](./08-principles.md). The 25 names that redirect an agent mid-task. 9. [Make it yours](./09-make-it-yours.md). Your own mode, `/correct` for repeated mistakes, and how to test a skill change. 10. [Recipes and pitfalls](./10-recipes-and-pitfalls.md). Prompts to copy and mistakes to skip. diff --git a/pstack/skills/poteto-mode/SKILL.md b/pstack/skills/poteto-mode/SKILL.md index 21993f546..d69d85109 100644 --- a/pstack/skills/poteto-mode/SKILL.md +++ b/pstack/skills/poteto-mode/SKILL.md @@ -58,6 +58,7 @@ Read the leaf skill in full for any principle you apply. Each entry names when i - **Boundary Discipline.** Wiring validation, error handling, or framework adapters. Guards at system boundaries, trust internal types, keep business logic pure. - **Type System Discipline.** Designing types or a signature in any typed language. Make illegal states unrepresentable, brand primitives, parse external data at boundaries. - **Make Operations Idempotent.** Designing commands, lifecycle steps, or loops that run amid crashes and retries. Converge to the same end state. +- **Bound Every External Call.** Calling a service, database, queue, third-party API, or subprocess you do not control. Set a deadline, retry only safe and transient failures with backoff, dead-letter poison messages, and break the circuit on a failing dependency. - **Migrate Callers Then Delete Legacy APIs.** Introducing a new internal API while old callers exist. Migrate and delete in one wave. - **Separate Before Serializing Shared State.** Concurrent actors might write the same file, branch, key, or object. Eliminate the sharing first. diff --git a/pstack/skills/principle-bound-every-external-call/SKILL.md b/pstack/skills/principle-bound-every-external-call/SKILL.md new file mode 100644 index 000000000..2f0b5b6cb --- /dev/null +++ b/pstack/skills/principle-bound-every-external-call/SKILL.md @@ -0,0 +1,28 @@ +--- +name: principle-bound-every-external-call +description: "Apply when code calls something it does not control: a network service, a database, a queue, a third-party API, or a subprocess. Give every call a deadline, retry only what is safe to retry, park messages that keep failing, and stop calling a dependency that is down." +disable-model-invocation: true +--- + +# Bound Every External Call + +Every call to a dependency you do not control can hang, fail, or succeed slowly. Decide what the code does in each case before you write the call, and make each answer a bound the code enforces. + +**Why:** An unbounded call turns one slow dependency into an outage everywhere it is called. A call with no deadline holds a thread or a connection until something else times out. A naive retry multiplies the load on a dependency that is already failing. A message that always fails blocks the queue behind it or disappears. + +**The idea:** give each external call four bounds. + +1. **A deadline.** Set an explicit timeout on every call, shorter than the budget of whoever waits on it. A library default is not a decision. Pass the remaining budget down so an inner call cannot outlive its caller. +2. **A retry policy.** Retry only errors that can be transient, such as timeouts, throttling, and 5xx responses, and only operations that are safe to repeat (see **make-operations-idempotent**). Use exponential backoff with jitter and a cap on attempts. Retry at one layer only. Retries stacked at several layers multiply into a retry storm. +3. **A place for poison messages.** In async work, move a message that keeps failing to a dead-letter queue after the attempt cap, with enough context to replay it. Do not drop it, and do not let it block the messages behind it. +4. **A circuit breaker.** When a dependency fails repeatedly, stop calling it for a while and fail fast or degrade on purpose. Probe it before you send full traffic again. + +Make each bound visible where the call is made, as named configuration or a typed client, not as library defaults that a reader cannot see. + +**The test:** +1. What happens if the dependency never answers? +2. What happens if it fails for ten minutes, then recovers? +3. Is every retried operation safe to run twice? +4. Where does a message go after its last attempt, and how is it replayed? + +Prove each answer with a test that uses a fake dependency that hangs, fails, and recovers, not with a comment. If an answer is "it waits" or "it retries forever," the call is missing a bound.