@elementarno/eawf 0.5.2

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.
Files changed (48) hide show
  1. package/.claude-plugin/marketplace.json +18 -0
  2. package/.claude-plugin/plugin.json +19 -0
  3. package/README.md +45 -0
  4. package/agents/auditor.md +69 -0
  5. package/agents/domain-specialist.md +60 -0
  6. package/agents/executor.md +69 -0
  7. package/agents/operator.md +66 -0
  8. package/agents/planner.md +86 -0
  9. package/agents/polisher.md +64 -0
  10. package/agents/researcher.md +71 -0
  11. package/agents/reviewer.md +68 -0
  12. package/hooks/post_commit.sh +30 -0
  13. package/hooks/post_push.sh +30 -0
  14. package/hooks/pre_commit.sh +30 -0
  15. package/hooks/pre_compact.sh +30 -0
  16. package/hooks/pre_push.sh +30 -0
  17. package/hooks/session_end.sh +30 -0
  18. package/hooks/session_start.sh +30 -0
  19. package/hooks/subagent_stop.sh +30 -0
  20. package/hooks.json +88 -0
  21. package/package.json +18 -0
  22. package/skills/add-property-test/SKILL.md +31 -0
  23. package/skills/agent-dispatch/SKILL.md +33 -0
  24. package/skills/audit/SKILL.md +35 -0
  25. package/skills/blitz/SKILL.md +24 -0
  26. package/skills/coauthor/SKILL.md +30 -0
  27. package/skills/compress/SKILL.md +30 -0
  28. package/skills/design/SKILL.md +63 -0
  29. package/skills/differentiate/SKILL.md +23 -0
  30. package/skills/extract-function/SKILL.md +31 -0
  31. package/skills/extract-module/SKILL.md +31 -0
  32. package/skills/flow/SKILL.md +28 -0
  33. package/skills/graduate-research-code/SKILL.md +31 -0
  34. package/skills/init/SKILL.md +32 -0
  35. package/skills/math-explainer/SKILL.md +64 -0
  36. package/skills/memory/SKILL.md +32 -0
  37. package/skills/mockup/SKILL.md +30 -0
  38. package/skills/polish/SKILL.md +29 -0
  39. package/skills/prep/SKILL.md +43 -0
  40. package/skills/refactor-god-class/SKILL.md +31 -0
  41. package/skills/research/SKILL.md +45 -0
  42. package/skills/review/SKILL.md +31 -0
  43. package/skills/roadmap/SKILL.md +31 -0
  44. package/skills/security-review/SKILL.md +30 -0
  45. package/skills/ship/SKILL.md +39 -0
  46. package/skills/spike/SKILL.md +65 -0
  47. package/skills/wave-spec/SKILL.md +28 -0
  48. package/skills/write-adr/SKILL.md +30 -0
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Eä-managed Claude Code hook wrapper for post_push.
3
+ # Generator: eawf plugin install claude (managed file — re-render via
4
+ # `eawf plugin update claude`; hand-edits are detected by `eawf plugin doctor`).
5
+ set -euo pipefail
6
+
7
+ # Synthesise a JSON payload from positional args (Claude passes args, not stdin).
8
+ # We escape only the minimum set of characters required for a JSON string body
9
+ # (backslash + double-quote) so the payload is portable across bash versions.
10
+ _eawf_json_escape() {
11
+ local s="${1-}"
12
+ s="${s//\\/\\\\}"
13
+ s="${s//\"/\\\"}"
14
+ printf '%s' "$s"
15
+ }
16
+
17
+ _eawf_arg1="$(_eawf_json_escape "${1-}")"
18
+ _eawf_arg2="$(_eawf_json_escape "${2-}")"
19
+ _eawf_arg3="$(_eawf_json_escape "${3-}")"
20
+ _eawf_arg4="$(_eawf_json_escape "${4-}")"
21
+
22
+ _eawf_payload=$(printf '{"hook_event_name":"%s","claude_event_name":"%s","args":["%s","%s","%s","%s"]}' \
23
+ "PostToolUse" \
24
+ "post_push" \
25
+ "${_eawf_arg1}" \
26
+ "${_eawf_arg2}" \
27
+ "${_eawf_arg3}" \
28
+ "${_eawf_arg4}")
29
+
30
+ printf '%s' "${_eawf_payload}" | exec uv run eawf hook run post_push --runtime claude
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Eä-managed Claude Code hook wrapper for pre_commit.
3
+ # Generator: eawf plugin install claude (managed file — re-render via
4
+ # `eawf plugin update claude`; hand-edits are detected by `eawf plugin doctor`).
5
+ set -euo pipefail
6
+
7
+ # Synthesise a JSON payload from positional args (Claude passes args, not stdin).
8
+ # We escape only the minimum set of characters required for a JSON string body
9
+ # (backslash + double-quote) so the payload is portable across bash versions.
10
+ _eawf_json_escape() {
11
+ local s="${1-}"
12
+ s="${s//\\/\\\\}"
13
+ s="${s//\"/\\\"}"
14
+ printf '%s' "$s"
15
+ }
16
+
17
+ _eawf_arg1="$(_eawf_json_escape "${1-}")"
18
+ _eawf_arg2="$(_eawf_json_escape "${2-}")"
19
+ _eawf_arg3="$(_eawf_json_escape "${3-}")"
20
+ _eawf_arg4="$(_eawf_json_escape "${4-}")"
21
+
22
+ _eawf_payload=$(printf '{"hook_event_name":"%s","claude_event_name":"%s","args":["%s","%s","%s","%s"]}' \
23
+ "PreToolUse" \
24
+ "pre_commit" \
25
+ "${_eawf_arg1}" \
26
+ "${_eawf_arg2}" \
27
+ "${_eawf_arg3}" \
28
+ "${_eawf_arg4}")
29
+
30
+ printf '%s' "${_eawf_payload}" | exec uv run eawf hook run pre_commit --runtime claude
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Eä-managed Claude Code hook wrapper for pre_compact.
3
+ # Generator: eawf plugin install claude (managed file — re-render via
4
+ # `eawf plugin update claude`; hand-edits are detected by `eawf plugin doctor`).
5
+ set -euo pipefail
6
+
7
+ # Synthesise a JSON payload from positional args (Claude passes args, not stdin).
8
+ # We escape only the minimum set of characters required for a JSON string body
9
+ # (backslash + double-quote) so the payload is portable across bash versions.
10
+ _eawf_json_escape() {
11
+ local s="${1-}"
12
+ s="${s//\\/\\\\}"
13
+ s="${s//\"/\\\"}"
14
+ printf '%s' "$s"
15
+ }
16
+
17
+ _eawf_arg1="$(_eawf_json_escape "${1-}")"
18
+ _eawf_arg2="$(_eawf_json_escape "${2-}")"
19
+ _eawf_arg3="$(_eawf_json_escape "${3-}")"
20
+ _eawf_arg4="$(_eawf_json_escape "${4-}")"
21
+
22
+ _eawf_payload=$(printf '{"hook_event_name":"%s","claude_event_name":"%s","args":["%s","%s","%s","%s"]}' \
23
+ "PreCompact" \
24
+ "pre_compact" \
25
+ "${_eawf_arg1}" \
26
+ "${_eawf_arg2}" \
27
+ "${_eawf_arg3}" \
28
+ "${_eawf_arg4}")
29
+
30
+ printf '%s' "${_eawf_payload}" | exec uv run eawf hook run pre_compact --runtime claude
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Eä-managed Claude Code hook wrapper for pre_push.
3
+ # Generator: eawf plugin install claude (managed file — re-render via
4
+ # `eawf plugin update claude`; hand-edits are detected by `eawf plugin doctor`).
5
+ set -euo pipefail
6
+
7
+ # Synthesise a JSON payload from positional args (Claude passes args, not stdin).
8
+ # We escape only the minimum set of characters required for a JSON string body
9
+ # (backslash + double-quote) so the payload is portable across bash versions.
10
+ _eawf_json_escape() {
11
+ local s="${1-}"
12
+ s="${s//\\/\\\\}"
13
+ s="${s//\"/\\\"}"
14
+ printf '%s' "$s"
15
+ }
16
+
17
+ _eawf_arg1="$(_eawf_json_escape "${1-}")"
18
+ _eawf_arg2="$(_eawf_json_escape "${2-}")"
19
+ _eawf_arg3="$(_eawf_json_escape "${3-}")"
20
+ _eawf_arg4="$(_eawf_json_escape "${4-}")"
21
+
22
+ _eawf_payload=$(printf '{"hook_event_name":"%s","claude_event_name":"%s","args":["%s","%s","%s","%s"]}' \
23
+ "PreToolUse" \
24
+ "pre_push" \
25
+ "${_eawf_arg1}" \
26
+ "${_eawf_arg2}" \
27
+ "${_eawf_arg3}" \
28
+ "${_eawf_arg4}")
29
+
30
+ printf '%s' "${_eawf_payload}" | exec uv run eawf hook run pre_push --runtime claude
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Eä-managed Claude Code hook wrapper for session_end.
3
+ # Generator: eawf plugin install claude (managed file — re-render via
4
+ # `eawf plugin update claude`; hand-edits are detected by `eawf plugin doctor`).
5
+ set -euo pipefail
6
+
7
+ # Synthesise a JSON payload from positional args (Claude passes args, not stdin).
8
+ # We escape only the minimum set of characters required for a JSON string body
9
+ # (backslash + double-quote) so the payload is portable across bash versions.
10
+ _eawf_json_escape() {
11
+ local s="${1-}"
12
+ s="${s//\\/\\\\}"
13
+ s="${s//\"/\\\"}"
14
+ printf '%s' "$s"
15
+ }
16
+
17
+ _eawf_arg1="$(_eawf_json_escape "${1-}")"
18
+ _eawf_arg2="$(_eawf_json_escape "${2-}")"
19
+ _eawf_arg3="$(_eawf_json_escape "${3-}")"
20
+ _eawf_arg4="$(_eawf_json_escape "${4-}")"
21
+
22
+ _eawf_payload=$(printf '{"hook_event_name":"%s","claude_event_name":"%s","args":["%s","%s","%s","%s"]}' \
23
+ "SessionEnd" \
24
+ "session_end" \
25
+ "${_eawf_arg1}" \
26
+ "${_eawf_arg2}" \
27
+ "${_eawf_arg3}" \
28
+ "${_eawf_arg4}")
29
+
30
+ printf '%s' "${_eawf_payload}" | exec uv run eawf hook run session_end --runtime claude
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Eä-managed Claude Code hook wrapper for session_start.
3
+ # Generator: eawf plugin install claude (managed file — re-render via
4
+ # `eawf plugin update claude`; hand-edits are detected by `eawf plugin doctor`).
5
+ set -euo pipefail
6
+
7
+ # Synthesise a JSON payload from positional args (Claude passes args, not stdin).
8
+ # We escape only the minimum set of characters required for a JSON string body
9
+ # (backslash + double-quote) so the payload is portable across bash versions.
10
+ _eawf_json_escape() {
11
+ local s="${1-}"
12
+ s="${s//\\/\\\\}"
13
+ s="${s//\"/\\\"}"
14
+ printf '%s' "$s"
15
+ }
16
+
17
+ _eawf_arg1="$(_eawf_json_escape "${1-}")"
18
+ _eawf_arg2="$(_eawf_json_escape "${2-}")"
19
+ _eawf_arg3="$(_eawf_json_escape "${3-}")"
20
+ _eawf_arg4="$(_eawf_json_escape "${4-}")"
21
+
22
+ _eawf_payload=$(printf '{"hook_event_name":"%s","claude_event_name":"%s","args":["%s","%s","%s","%s"]}' \
23
+ "SessionStart" \
24
+ "session_start" \
25
+ "${_eawf_arg1}" \
26
+ "${_eawf_arg2}" \
27
+ "${_eawf_arg3}" \
28
+ "${_eawf_arg4}")
29
+
30
+ printf '%s' "${_eawf_payload}" | exec uv run eawf hook run session_start --runtime claude
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Eä-managed Claude Code hook wrapper for subagent_stop.
3
+ # Generator: eawf plugin install claude (managed file — re-render via
4
+ # `eawf plugin update claude`; hand-edits are detected by `eawf plugin doctor`).
5
+ set -euo pipefail
6
+
7
+ # Synthesise a JSON payload from positional args (Claude passes args, not stdin).
8
+ # We escape only the minimum set of characters required for a JSON string body
9
+ # (backslash + double-quote) so the payload is portable across bash versions.
10
+ _eawf_json_escape() {
11
+ local s="${1-}"
12
+ s="${s//\\/\\\\}"
13
+ s="${s//\"/\\\"}"
14
+ printf '%s' "$s"
15
+ }
16
+
17
+ _eawf_arg1="$(_eawf_json_escape "${1-}")"
18
+ _eawf_arg2="$(_eawf_json_escape "${2-}")"
19
+ _eawf_arg3="$(_eawf_json_escape "${3-}")"
20
+ _eawf_arg4="$(_eawf_json_escape "${4-}")"
21
+
22
+ _eawf_payload=$(printf '{"hook_event_name":"%s","claude_event_name":"%s","args":["%s","%s","%s","%s"]}' \
23
+ "SubagentStop" \
24
+ "subagent_stop" \
25
+ "${_eawf_arg1}" \
26
+ "${_eawf_arg2}" \
27
+ "${_eawf_arg3}" \
28
+ "${_eawf_arg4}")
29
+
30
+ printf '%s' "${_eawf_payload}" | exec uv run eawf hook run subagent_stop --runtime claude
package/hooks.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/post_commit.sh",
8
+ "type": "command"
9
+ }
10
+ ],
11
+ "matcher": "Bash"
12
+ },
13
+ {
14
+ "hooks": [
15
+ {
16
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/post_push.sh",
17
+ "type": "command"
18
+ }
19
+ ],
20
+ "matcher": "Bash"
21
+ }
22
+ ],
23
+ "PreCompact": [
24
+ {
25
+ "hooks": [
26
+ {
27
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre_compact.sh",
28
+ "type": "command"
29
+ }
30
+ ],
31
+ "matcher": ""
32
+ }
33
+ ],
34
+ "PreToolUse": [
35
+ {
36
+ "hooks": [
37
+ {
38
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre_commit.sh",
39
+ "type": "command"
40
+ }
41
+ ],
42
+ "matcher": "Bash"
43
+ },
44
+ {
45
+ "hooks": [
46
+ {
47
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre_push.sh",
48
+ "type": "command"
49
+ }
50
+ ],
51
+ "matcher": "Bash"
52
+ }
53
+ ],
54
+ "SessionStart": [
55
+ {
56
+ "hooks": [
57
+ {
58
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/session_start.sh",
59
+ "type": "command"
60
+ }
61
+ ],
62
+ "matcher": ""
63
+ }
64
+ ],
65
+ "Stop": [
66
+ {
67
+ "hooks": [
68
+ {
69
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/session_end.sh",
70
+ "type": "command"
71
+ }
72
+ ],
73
+ "matcher": ""
74
+ }
75
+ ],
76
+ "SubagentStop": [
77
+ {
78
+ "hooks": [
79
+ {
80
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/subagent_stop.sh",
81
+ "type": "command"
82
+ }
83
+ ],
84
+ "matcher": ""
85
+ }
86
+ ]
87
+ }
88
+ }
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@elementarno/eawf",
3
+ "version": "0.5.2",
4
+ "description": "E\u00e4 Workflow \u2014 state-first, agent-driven development framework (Claude Code plugin).",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Elementarno9/eawf.git"
9
+ },
10
+ "files": [
11
+ ".claude-plugin/",
12
+ "skills/",
13
+ "agents/",
14
+ "hooks.json",
15
+ "hooks/",
16
+ "README.md"
17
+ ]
18
+ }
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: add-property-test
3
+ description: Model-only playbook for adding a property-based test that pins a function's invariant.
4
+ argument-hint: ""
5
+ user-invocable: false
6
+ disable-model-invocation: false
7
+ ---
8
+
9
+ # add-property-test
10
+
11
+ A model-only playbook for adding a property-based test. There is no slash command; the model invokes this when example-based tests leave a function's invariants under-specified.
12
+
13
+ ## When to reach for it
14
+
15
+ - The function has an algebraic law (round-trip, idempotence, commutativity, monotonicity) that examples only sample.
16
+ - The input space is large and edge cases keep slipping through hand- written cases.
17
+ - A parser/serialiser pair should satisfy `decode(encode(x)) == x`.
18
+
19
+ ## Canonical procedure
20
+
21
+ 1. State the invariant in one sentence ("encoding then decoding returns the input").
22
+ 2. Pick the narrowest strategy that generates valid inputs; constrain it so generated values are in-domain rather than filtering after the fact.
23
+ 3. Assert the law inside the test body; let the framework shrink failures to a minimal counter-example.
24
+ 4. Keep one or two example-based tests alongside for the named boundary cases (empty, single, max-length) the contract calls out.
25
+ 5. When a property fails, treat the shrunk counter-example as a new regression fixture before fixing the code.
26
+
27
+ ## Guardrails
28
+
29
+ - Property tests complement, never replace, the boundary-case AND error-path tests every public function owes (test-discipline rule).
30
+ - Use `pytest.approx` for float laws and `numpy.testing.assert_allclose` for array laws.
31
+ - Name the test `test_<func>_<invariant>` so the law is legible from the test report.
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: agent-dispatch
3
+ description: Dispatch a claimed wave to a runtime per the V8 session-reuse ladder.
4
+ argument-hint: "<wave-id> [--runtime=<id>]"
5
+ user-invocable: true
6
+ disable-model-invocation: true
7
+ ---
8
+
9
+ # /agent-dispatch
10
+
11
+ ## v0.4 cross-links
12
+
13
+ Dispatch resolves the wave's `RoleSpec` (`role`, `model`, `tools`, `isolation`) and renders the subagent's prompt from the wave's `agent_role` + the role's canonical contract. The dispatched session emits one `agent_end` report carrying an `EvidenceRecord` summary; the recorded evidence feeds the wave's `CloseReadiness`. When the operator pins a runtime preference, the choice persists via a `MEMORY` mutation (`MutationKind=update`) on the `Project` row so the next dispatch starts from the operator's pinned ladder.
14
+
15
+ ## Canonical algorithm
16
+
17
+ 1. Resolve the target `wave_id` (required; a missing id degrades to `status=needs_user`).
18
+ 2. Read the `Wave.runtime_preference` ladder (or an explicit `runtime_preference` arg); an explicit `runtime` arg overrides the ladder head.
19
+ 3. Surface the full ladder and the resolved head. No resolvable runtime is a soft `status=partial` (the dispatch can still proceed against the daemon default, but the operator can pin a preference).
20
+ 4. The daemon's `agent.dispatch` RPC is the canonical mutator; the skill routes to `eawf wave dispatch` via `next_valid_actions`.
21
+
22
+ ## Pre-flight checklist
23
+
24
+ - [ ] The wave is claimed before dispatch.
25
+ - [ ] The runtime ladder reflects how the planner sized the wave.
26
+
27
+ ## Decision surfaces
28
+
29
+ A missing `wave_id` degrades to `status=needs_user`. When no runtime in the ladder resolves, the soft `status=partial` routes the operator to an `AskUserQuestion` prompt to pin a runtime preference rather than silently falling through to the daemon default.
30
+
31
+ ## Output contract
32
+
33
+ Skill envelope with `header.skill = "/agent-dispatch"`. Body carries wave_id, runtime_preference, and resolved_runtime.
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: audit
3
+ description: Fresh-context verification of a phase deliverable or wave outcome. Spawns a fresh auditor subagent that re-reads the diff against the success criteria.
4
+ argument-hint: "<phase-id|wave-id|commit-range>"
5
+ user-invocable: true
6
+ disable-model-invocation: false
7
+ ---
8
+
9
+ # /audit
10
+
11
+ ## Canonical algorithm
12
+
13
+ 1. Resolve target: phase id, wave id, or commit range.
14
+ 2. Identify success criteria from the plan / phase spec and cite evidence with dense `[N]` references.
15
+ 3. Dispatch the auditor subagent with paths, line numbers, criteria.
16
+ 4. Parse the verdict; convert refutations into TODOs or new waves.
17
+ 5. Render audit evidence through `eawf audit show --md`.
18
+
19
+ ## v0.4 cross-links
20
+
21
+ The auditor emits one `EvidenceRecord` per criterion (`evidence_kind` = `gate` | `claim` | `decision`). The aggregate verdict folds into the target wave / iter `CloseReadiness` projection — `/ship` reads the same projection so audit and ship share one source of truth on "is this iter actually closable?". Roles are pinned via `RoleSpec` (`role="auditor"`); fresh-context isolation stays a `RoleSpec` invariant, not an ad-hoc dispatch flag.
22
+
23
+ ## Pre-flight checklist
24
+
25
+ - [ ] The auditor must NOT have access to the parent conversation.
26
+ - [ ] Every quantitative claim must include source evidence and dense citation refs.
27
+ - [ ] The target iter is NOT yet closed. Iter close is gated on `audit + polish + ship CI + PR review pass` per the `iter-phase-close-timing` rule in AGENTS.md; `/audit` runs before that close.
28
+
29
+ ## Decision surfaces
30
+
31
+ On `pass-with-followups`: present the follow-up disposition (open backlog, open wave, defer) through `AskUserQuestion`. On `fail`: ask whether to halt the flow or open a remediation wave.
32
+
33
+ ## Output contract
34
+
35
+ Skill envelope with a per-criterion verdict table and an aggregate status (`pass | pass-with-followups | fail`).
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: blitz
3
+ description: Auto-chained research follow-up skill with recursion guard for residual unknowns.
4
+ argument-hint: "[--residual-unknowns=<n>]"
5
+ user-invocable: true
6
+ disable-model-invocation: false
7
+ ---
8
+
9
+ # /blitz
10
+
11
+ ## Canonical algorithm
12
+
13
+ 1. Read residual unknown count and follow-up research args.
14
+ 2. Increment the recursion guard (`EAWF_BLITZ_DEPTH_COUNTER`) against `EAWF_BLITZ_DEPTH` (default 8).
15
+ 3. Return a follow-up `/research` action with `blitz=false` so the next research pass does not recurse indefinitely.
16
+
17
+ ## Pre-flight checklist
18
+
19
+ - [ ] Confirm `/research` produced more than one residual unknown.
20
+ - [ ] Confirm recursion depth has not exceeded the configured cap.
21
+
22
+ ## Output contract
23
+
24
+ Skill envelope with `header.skill = "/blitz"`. Body carries depth, depth_cap, residual_unknowns, followup_research_args, and next_actions.
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: coauthor
3
+ description: Resolve the Co-Authored-By trailer policy for the active repo.
4
+ argument-hint: "[--mode=runtime|project|disabled]"
5
+ user-invocable: true
6
+ disable-model-invocation: false
7
+ ---
8
+
9
+ # /coauthor
10
+
11
+ ## Canonical algorithm
12
+
13
+ 1. Read the `vcs.coauthor` config block (`mode` ∈ `runtime|project|disabled`) and the caller-supplied `mode` / `runtime` / `message_text` args.
14
+ 2. Run the canonical resolver (`resolve_coauthor_trailer`) to turn the config + the explicit runtime opt-in into a `Co-Authored-By:` trailer line (or `None` when trailers are disabled).
15
+ 3. On `mode=runtime` with no resolvable runtime (no explicit opt-in and no usable default identity), degrade to `status=needs_user` so the runtime adapter surfaces a `coauthor resolve` prompt rather than guessing.
16
+
17
+ This skill is a thin surface over the shipped co-author policy machinery; it does not reimplement trailer resolution.
18
+
19
+ ## Pre-flight checklist
20
+
21
+ - [ ] Never invent an author identity — `disabled` mode rejects an existing trailer in `message_text`.
22
+ - [ ] No PII beyond the canonical author block.
23
+
24
+ ## Decision surfaces
25
+
26
+ `status=needs_user` routes to a `coauthor resolve` `AskUserQuestion` when the runtime cannot be resolved.
27
+
28
+ ## Output contract
29
+
30
+ Skill envelope with `header.skill = "/coauthor"`. Body carries mode, runtime, and the resolved trailer (or a reason on the needs_user path).
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: compress
3
+ description: Compress the session conversation when context approaches the limit.
4
+ argument-hint: "[--tokens-before=<n>] [--tokens-after=<n>] [--runtime=<id>]"
5
+ user-invocable: true
6
+ disable-model-invocation: false
7
+ ---
8
+
9
+ # /compress
10
+
11
+ ## Canonical algorithm
12
+
13
+ 1. Read `tokens_before` (required; missing/zero degrades to `status=needs_user`) and `tokens_after` (defaults to a no-op when omitted; clamped so a pass can only shrink the context).
14
+ 2. Build the per-runtime compression directive (cache-control wiring) for the target `runtime` (defaults to `claude-code`, the only runtime with a caller-side cache-control marker). An unknown runtime degrades to `status=needs_user`.
15
+ 3. Append the canonical `compression_emitted` event carrying the token deltas and the realised ratio so the telemetry projector can chart context pressure over a session.
16
+
17
+ The model summarisation fan-out lives behind the runtime adapter's cache-control hook; the skill records the requested compression.
18
+
19
+ ## Pre-flight checklist
20
+
21
+ - [ ] `tokens_before` is present and > 0.
22
+ - [ ] The target runtime is a known runtime id.
23
+
24
+ ## Decision surfaces
25
+
26
+ A missing/zero `tokens_before` or an unknown `runtime` degrades to `status=needs_user`, which routes the operator to an `AskUserQuestion` prompt for the missing input rather than emitting a compression event against an unresolved runtime.
27
+
28
+ ## Output contract
29
+
30
+ Skill envelope with `header.skill = "/compress"`. Body carries tokens_before, tokens_after, ratio, runtime, and cache_control_applied.
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: design
3
+ description: Read-only design pass for an interactive surface: produces a statechart + action x context matrix + journey scenarios backed by an 11-rule lint contract. No state mutations.
4
+ argument-hint: "<surface-slug> [--final] [--from-brief <path>]"
5
+ user-invocable: true
6
+ disable-model-invocation: true
7
+ ---
8
+
9
+ # /design
10
+
11
+ ## Purpose
12
+
13
+ Prevent the P20-I03 class of failure: a phase that audit-passes its declared criteria but breaks the lived user experience at runtime because a transition, a refresh trigger, or a multi-keystroke dispatch was never enumerated in the design step. `/design` produces a triangulated design spec — statechart + action x context matrix + journey scenarios — backed by a six-category event taxonomy, per-view liveness contracts, a multi-keystroke substate rule, cross-component contracts, and an 11-rule lint contract. It runs after `/research --final`, before `/roadmap propose`. Read-only: it writes only under `.ea/local/`.
14
+
15
+ ## Canonical algorithm
16
+
17
+ 1. Resolve the surface slug from the argument (or `AskUserQuestion` if absent). The slug suffixes the artifact stem: `<YYYY-MM-DD>-<surface>-design.md`.
18
+ 2. Round 1 — personas + goals. AUQ batch; every selected persona has
19
+ >=1 goal and every (persona, goal) gets a one-sentence success
20
+ criterion.
21
+ 3. Round 2 — event sources + statechart + liveness contracts. AUQ batch. The six-category event taxonomy is mandatory; every long-lived view requires a liveness contract row; every multi-char key sequence requires a substate chain (never a flat `KEY_oH`).
22
+ 4. Round 3 — action x context matrix. AUQ batch. No blank cells (`-` is the only legal intentional no-op); matrix columns are parity-locked to statechart states.
23
+ 5. Round 4 — journey scenarios. AUQ batch. >=1 scenario per (persona x goal), >=1 time-advance scenario per long-lived view, plus edge scenarios per declared failure mode.
24
+ 6. Self-lint. Walk the 11-rule lint contract (L1..L11); round-close is gated on a lint pass.
25
+ 7. Write `.ea/local/research/<YYYY-MM-DD>-<surface>-design.md` using the artifact chassis and return the output envelope. On `--final` with residual unknowns > 0, emit a `/blitz` follow-up under the standard recursion guard.
26
+
27
+ ## The three load-bearing rigour mechanisms
28
+
29
+ These are the additions over a generic statechart-plus-matrix doc; each catches one concrete P20-I03 defect at design-review time:
30
+
31
+ - Per-view liveness contract — caught the live git pane that never rebound to a refresh trigger.
32
+ - Six-category event taxonomy — caught the missing periodic tick (round 2 rejects close when a long-lived view declares no refresh trigger).
33
+ - Multi-keystroke substate rule — caught the verb-prefix sequence wired flat (lint L6 rejects flat `KEY_oH` entries; the substate chain is mandatory).
34
+
35
+ ## Lint contract (L1..L11)
36
+
37
+ Until the `eawf design lint <path>` binary lands, the skill agent walks the rules in-conversation at the end of rounds 3 and 4. A round does not close while any rule fails. The rules cover statechart/matrix parity (L1, L2), no surprise events (L3), no unfilled cells (L4), liveness-row completeness (L5), substate-backed multi-char keys (L6), scenario coverage (L7, L8), scrub-clean citations (L9), cross-component rows resolving to real test paths (L10), and append-only revisions (L11).
38
+
39
+ ## Design to audit loop
40
+
41
+ When a phase touches an interactive surface, the ship-gate audit binds to the design artifact and adds criteria A1..A4 (every statechart `on:` clause, every non-`-` matrix cell, every Gherkin scenario, and every liveness contract has a covering test). Without this loop the design artifact becomes write-once advisory and the P20-I03 class recurs.
42
+
43
+ ## Pre-flight checklist
44
+
45
+ - [ ] No state mutations — read-only; writes only under `.ea/local/`.
46
+ - [ ] Surface slug matches the future wave / iter / phase prefix so dispatch renderers surface this brief.
47
+ - [ ] Event taxonomy populated for all six categories (present or N/A with a one-sentence reason).
48
+ - [ ] Statechart `on:` clauses complete; no named state has an unhandled named event.
49
+ - [ ] Every long-lived view has a liveness contract row with all five fields.
50
+ - [ ] Every multi-char key sequence is a substate chain — no flat `KEY_oH` entries.
51
+ - [ ] Matrix has no blank cells; `-` is the only legal no-op value.
52
+ - [ ] >=1 journey scenario per (persona x goal); >=1 time-advance scenario per long-lived view; edge scenarios per declared edge category.
53
+ - [ ] Cross-component contracts list every cross-module event with its integration test path.
54
+ - [ ] Lint L1..L11 all pass.
55
+ - [ ] Citations repo-relative, external URL, or eawf URN — no absolute local paths, no host-local URLs, no PII.
56
+
57
+ ## Decision surfaces
58
+
59
+ Every round's picks (personas, goals, event sources, statechart shape, matrix cells, scenarios) are surfaced through `AskUserQuestion` batches, never free-text. `/design` is convention-only: no state field, no `/roadmap propose` hard gate. The artifact is cited from the roadmap proposal body and from each wave dispatch prompt; dispatch renderers surface design artifacts under a `## Design references` section by filename match.
60
+
61
+ ## Output contract
62
+
63
+ Eä-rendered skill envelope (`OutputEnvelope`) with `header.skill = "/design"`. Body carries the surface slug, the artifact path, the rounds completed, the declared / N/A event sources, the screen + long-lived-view counts, the matrix fill tally, the scenario count, the lint status, and any residual unknowns.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: differentiate
3
+ description: Recommend the cheapest experiment that discriminates between two or more candidate paths.
4
+ argument-hint: "<candidate-id>"
5
+ user-invocable: true
6
+ disable-model-invocation: false
7
+ ---
8
+
9
+ # /differentiate
10
+
11
+ ## Canonical algorithm
12
+
13
+ 1. Take a candidate path and the alternatives under consideration.
14
+ 2. Identify the discriminating signal (`what would change my mind?`).
15
+ 3. Recommend the cheapest experiment that produces the signal.
16
+
17
+ ## Pre-flight checklist
18
+
19
+ - [ ] Read-only — no state mutations.
20
+
21
+ ## Output contract
22
+
23
+ Skill envelope listing the discriminating experiments and the recommended next move.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: extract-function
3
+ description: Model-only refactoring playbook for pulling a coherent block out of a long function into a named helper.
4
+ argument-hint: ""
5
+ user-invocable: false
6
+ disable-model-invocation: false
7
+ ---
8
+
9
+ # extract-function
10
+
11
+ A model-only refactoring playbook. There is no slash command; the model invokes this to pull a coherent block out of a long function into a named helper.
12
+
13
+ ## When to reach for it
14
+
15
+ - A function spans more than one screen and reads as several phases.
16
+ - A comment introduces a block ("# now normalise the rows") — the comment is begging to become a function name.
17
+ - The same computation appears in two places (DRY).
18
+
19
+ ## Canonical procedure
20
+
21
+ 1. Identify the block and the minimal set of variables it reads (inputs) and the single value it produces (output). If it produces two, that is two extractions.
22
+ 2. Name the helper for WHAT it returns, not how — `normalised_rows`, not `do_step_two`.
23
+ 3. Lift the block into a pure function where viable: explicit args in, explicit value out, no hidden state mutation.
24
+ 4. Replace the original block with a call; keep the surrounding function's shape so the diff is reviewable.
25
+ 5. Give the new helper full type hints, a docstring with a `Raises:` block if it can raise, and boundary + error-path tests if it is public.
26
+
27
+ ## Guardrails
28
+
29
+ - Stop and reconsider if the helper needs four or more parameters — that often signals a missing value object, not a missing function.
30
+ - Three similar lines are fine; do not extract a one-line helper used once (YAGNI / KISS).
31
+ - Use named arguments once arity reaches three (explicit-over-implicit).