@techgoblin/gobstack 0.0.0-stage → 0.4.4-beta.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 (108) hide show
  1. package/CHANGELOG.md +351 -0
  2. package/LICENSE +21 -0
  3. package/README.md +217 -2
  4. package/VERSION +1 -0
  5. package/adapters/_template/adapter.tsv +16 -0
  6. package/adapters/_template/detect.sh +10 -0
  7. package/adapters/_template/emit.sh +5 -0
  8. package/adapters/_template/verify.sh +4 -0
  9. package/adapters/claude/adapter.tsv +8 -0
  10. package/adapters/claude/detect.sh +8 -0
  11. package/adapters/claude/verify.sh +47 -0
  12. package/adapters/codex/adapter.tsv +12 -0
  13. package/adapters/codex/detect.sh +9 -0
  14. package/adapters/codex/verify.sh +45 -0
  15. package/adapters/copilot/adapter.tsv +10 -0
  16. package/adapters/copilot/detect.sh +8 -0
  17. package/adapters/copilot/verify.sh +45 -0
  18. package/adapters/cursor/adapter.tsv +11 -0
  19. package/adapters/cursor/detect.sh +10 -0
  20. package/adapters/cursor/verify.sh +45 -0
  21. package/adapters/gemini/adapter.tsv +15 -0
  22. package/adapters/gemini/detect.sh +11 -0
  23. package/adapters/gemini/verify.sh +49 -0
  24. package/adapters/hermes/adapter.tsv +9 -0
  25. package/adapters/hermes/detect.sh +8 -0
  26. package/adapters/hermes/verify.sh +27 -0
  27. package/adapters/opencode/adapter.tsv +14 -0
  28. package/adapters/opencode/detect.sh +9 -0
  29. package/adapters/opencode/verify.sh +45 -0
  30. package/automations/README.md +53 -0
  31. package/automations/bugreporter-intake.sh +145 -0
  32. package/automations/drift-audit.sh +139 -0
  33. package/automations/report.schema.tsv +10 -0
  34. package/bans/README.md +82 -0
  35. package/bans/grep-ban.sh +84 -0
  36. package/bans/layer-check.sh +57 -0
  37. package/bin/goblin +119 -0
  38. package/bin/goblin-audit +145 -0
  39. package/bin/goblin-bans +178 -0
  40. package/bin/goblin-doctor +233 -0
  41. package/bin/goblin-emit +484 -0
  42. package/bin/goblin-init +519 -0
  43. package/bin/goblin-install +720 -0
  44. package/bin/goblin-lib.sh +289 -0
  45. package/bin/goblin-model +105 -0
  46. package/bin/goblin-upgrade +572 -0
  47. package/bin/goblin-verify +2798 -0
  48. package/bin/goblin.js +103 -0
  49. package/docs/ADOPTION.md +168 -0
  50. package/docs/CI.md +187 -0
  51. package/docs/CONTRACTS.md +197 -0
  52. package/docs/DESIGN.md +92 -0
  53. package/docs/ENFORCEMENT.md +225 -0
  54. package/docs/FLOWS.md +164 -0
  55. package/docs/GUARDRAILS.md +126 -0
  56. package/docs/GUIDE.md +610 -0
  57. package/docs/INTEGRATION.md +92 -0
  58. package/docs/LIMITS.md +591 -0
  59. package/docs/LOOP.md +165 -0
  60. package/docs/RE-PLAYBOOK.md +183 -0
  61. package/docs/RISKS.md +70 -0
  62. package/docs/ROLES.md +105 -0
  63. package/manifest/bans.tsv +9 -0
  64. package/manifest/classes.tsv +61 -0
  65. package/manifest/enforcement.tsv +88 -0
  66. package/manifest/glossary.tsv +25 -0
  67. package/manifest/playbooks.tsv +16 -0
  68. package/package.json +37 -4
  69. package/presets/A-shipped-software.yaml +48 -0
  70. package/presets/B-service-config.yaml +40 -0
  71. package/presets/C-game.yaml +38 -0
  72. package/presets/D-knowledge.yaml +41 -0
  73. package/presets/E-fleet-config.yaml +42 -0
  74. package/presets/F-electron.yaml +67 -0
  75. package/roles.yaml +54 -0
  76. package/skills/goblin-bootstrap/SKILL.md +51 -0
  77. package/skills/goblin-bugfix/SKILL.md +26 -0
  78. package/skills/goblin-bugreporter/SKILL.md +52 -0
  79. package/skills/goblin-drift-audit/SKILL.md +43 -0
  80. package/skills/goblin-eval/SKILL.md +68 -0
  81. package/skills/goblin-feature/SKILL.md +26 -0
  82. package/skills/goblin-feature-map/SKILL.md +140 -0
  83. package/skills/goblin-handoff/SKILL.md +28 -0
  84. package/skills/goblin-investigation/SKILL.md +26 -0
  85. package/skills/goblin-judge/SKILL.md +74 -0
  86. package/skills/goblin-loop/SKILL.md +88 -0
  87. package/skills/goblin-mode/SKILL.md +70 -0
  88. package/skills/goblin-overnight/SKILL.md +42 -0
  89. package/skills/goblin-pr-gate/SKILL.md +42 -0
  90. package/skills/goblin-re-mobile/SKILL.md +51 -0
  91. package/skills/goblin-refactor/SKILL.md +23 -0
  92. package/skills/goblin-sweep/SKILL.md +23 -0
  93. package/skills/goblin-tdd-repro/SKILL.md +27 -0
  94. package/skills/goblin-verify-author/SKILL.md +50 -0
  95. package/skills/practice/SKILL.md +37 -0
  96. package/templates/AGENTS.md.tmpl +23 -0
  97. package/templates/HANDOFF.md.tmpl +43 -0
  98. package/templates/SPEC.md.tmpl +34 -0
  99. package/templates/audit-waiver.tsv.tmpl +10 -0
  100. package/templates/boundary-waivers.tmpl +8 -0
  101. package/templates/checks/assert.mjs.tmpl +60 -0
  102. package/templates/checks/gate.sh.tmpl +29 -0
  103. package/templates/ci/goblin-gate.yml.tmpl +46 -0
  104. package/templates/goblin.yaml.tmpl +138 -0
  105. package/templates/install-hooks.allowlist.tmpl +9 -0
  106. package/templates/loop/decisions.tsv.tmpl +1 -0
  107. package/templates/loop/predicate.tmpl +16 -0
  108. package/templates/report.yaml.tmpl +16 -0
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/_template/verify.sh — W5+ stub. Refuses until the adapter is built.
3
+ printf 'verify: template adapter - not implemented (copy adapters/_template and fill in the anchors)\n' >&2
4
+ exit 2
@@ -0,0 +1,8 @@
1
+ # adapters/claude/adapter.tsv - W4A-SPEC SS2/SS8 P1. Shapes from Claude Code's own docs
2
+ # (code.claude.com/docs/en/skills + /memory, read 2026-09-29): skills are <name>/SKILL.md under
3
+ # .claude/skills/ (project) and ~/.claude/skills/ (personal), name+description frontmatter
4
+ # required to index; project memory is repo-root CLAUDE.md (the docs' project-instructions
5
+ # location; .claude/CLAUDE.md is listed as an alternate). settings.json and hooks are
6
+ # deliberately untouched (W4A-SPEC SS10.3).
7
+ id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
8
+ claude Claude Code ~/.claude:claude .claude/skills/<name>/SKILL.md ~/.claude/skills/<name>/SKILL.md CLAUDE.md https://code.claude.com/docs/en/skills 2026-09-29 yes full yes no yes
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/claude/detect.sh — the detection contract (W4A-SPEC §3).
3
+ # Prints the detected anchor on stdout; exits 0 present / 1 absent.
4
+ # Side-effect-free, offline, read-only. DETECTED iff ~/.claude/ exists or `claude` is on PATH.
5
+ set -u
6
+ [ -d "${HOME:-}/.claude" ] && { printf '%s\n' "${HOME}/.claude"; exit 0; }
7
+ if command -v claude >/dev/null 2>&1; then printf '%s\n' "$(command -v claude)"; exit 0; fi
8
+ exit 1
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/claude/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
3
+ # to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
4
+ # byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
5
+ # 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
6
+ set -uo pipefail
7
+ SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
8
+ SRC=$(cd "$SELF_DIR/../.." && pwd)
9
+ TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
10
+ VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
11
+ CF="$TARGET/CLAUDE.md"
12
+ SKROOT="$TARGET/.claude/skills"
13
+ bad=0
14
+
15
+ # the block: the on-disk goblin block must byte-equal what emit writes now at this VERSION
16
+ if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
17
+ EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
18
+ index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
19
+ WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
20
+ WANT="$WANT
21
+ goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
22
+ Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
23
+ goblin-verify-author, practice. Invoke a skill by name when the task matches its
24
+ description; the full procedure lives in each SKILL.md.
25
+ <!-- goblin-stack:end -->"
26
+ if [ "$EXPECT" != "$WANT" ]; then
27
+ printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
28
+ bad=1
29
+ fi
30
+ fi
31
+
32
+ # every emitted goblin skill must be byte-identical to the source payload
33
+ if [ -d "$SKROOT" ]; then
34
+ for d in "$SKROOT"/*/; do
35
+ [ -d "$d" ] || continue
36
+ name=$(basename "$d")
37
+ case "$name" in goblin-*|practice) ;; *) continue ;; esac
38
+ f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
39
+ if [ ! -f "$f" ]; then continue; fi
40
+ if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
41
+ printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
42
+ bad=1
43
+ fi
44
+ done
45
+ fi
46
+ [ "$bad" -eq 0 ] && printf 'verified: claude emission matches v%s byte-for-byte\n' "$VERSION"
47
+ exit "$bad"
@@ -0,0 +1,12 @@
1
+ # adapters/codex/adapter.tsv - W4B. Shapes from OpenAI Codex's own docs
2
+ # (developers.openai.com/codex/skills, read 2026-09-29): skills are <name>/SKILL.md under
3
+ # .agents/skills/ (repo - scanned from $CWD up to $REPO_ROOT) and ~/.agents/skills/ (USER);
4
+ # the docs name no .codex/skills/ directory for skills (the .codex/codex compatibility root is
5
+ # Cursor's fallback, not Codex's own docs). Context: AGENTS.md in the project root is Codex's
6
+ # instruction file (the agents.md convention Codex originated; the docs' skills page contrasts
7
+ # persistent context with on-demand skills). Capabilities: [[skills.config]] in
8
+ # ~/.codex/config.toml can disable a skill (docs, same date) - command-level blocking is
9
+ # approvals, recorded partial; distribution is the plugin directory (marketplace yes, the
10
+ # docs' own channel); no post-compaction hook is documented.
11
+ id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
12
+ codex OpenAI Codex CLI ~/.codex:codex .agents/skills/<name>/SKILL.md ~/.agents/skills/<name>/SKILL.md AGENTS.md https://developers.openai.com/codex/skills 2026-09-29 yes full partial yes no
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/codex/detect.sh — the detection contract (W4A-SPEC §3).
3
+ # Prints the detected anchor on stdout; exits 0 present / 1 absent.
4
+ # Side-effect-free, offline, read-only. DETECTED iff ~/.codex/ exists or the codex CLI
5
+ # is on PATH.
6
+ set -u
7
+ [ -d "${HOME:-}/.codex" ] && { printf '%s\n' "${HOME}/.codex"; exit 0; }
8
+ if command -v codex >/dev/null 2>&1; then printf '%s\n' "$(command -v codex)"; exit 0; fi
9
+ exit 1
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/codex/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
3
+ # to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
4
+ # byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
5
+ # 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
6
+ set -uo pipefail
7
+ SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
8
+ SRC=$(cd "$SELF_DIR/../.." && pwd)
9
+ TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
10
+ VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
11
+ CF="$TARGET/AGENTS.md"
12
+ SKROOT="$TARGET/.agents/skills"
13
+ bad=0
14
+
15
+ if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
16
+ EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
17
+ index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
18
+ WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
19
+ WANT="$WANT
20
+ goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
21
+ Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
22
+ goblin-verify-author, practice. Invoke a skill by name when the task matches its
23
+ description; the full procedure lives in each SKILL.md.
24
+ <!-- goblin-stack:end -->"
25
+ if [ "$EXPECT" != "$WANT" ]; then
26
+ printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
27
+ bad=1
28
+ fi
29
+ fi
30
+
31
+ if [ -d "$SKROOT" ]; then
32
+ for d in "$SKROOT"/*/; do
33
+ [ -d "$d" ] || continue
34
+ name=$(basename "$d")
35
+ case "$name" in goblin-*|practice) ;; *) continue ;; esac
36
+ f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
37
+ if [ ! -f "$f" ]; then continue; fi
38
+ if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
39
+ printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
40
+ bad=1
41
+ fi
42
+ done
43
+ fi
44
+ [ "$bad" -eq 0 ] && printf 'verified: codex emission matches v%s byte-for-byte\n' "$VERSION"
45
+ exit "$bad"
@@ -0,0 +1,10 @@
1
+ # adapters/copilot/adapter.tsv - W4A-SPEC SS2/SS8 P2. Shapes from GitHub Copilot CLI's own docs
2
+ # (docs.github.com/en/copilot, read 2026-09-29): agent skills at <name>/SKILL.md under
3
+ # .github/skills/ (project - of the two candidate roots, this one is the documented Copilot CLI
4
+ # location; .agents/skills is the cross-tool convention, not the documented Copilot location)
5
+ # and ~/.copilot/skills/ (personal); repository-wide custom instructions are
6
+ # .github/copilot-instructions.md. Prompt hooks fire at sessionStart only, new interactive
7
+ # sessions - not on resume, not headless (hooks reference, same date): the plan's caveat,
8
+ # recorded in cap_post_compaction=no and printed by the doctor.
9
+ id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
10
+ copilot GitHub Copilot CLI ~/.copilot:copilot .github/skills/<name>/SKILL.md ~/.copilot/skills/<name>/SKILL.md .github/copilot-instructions.md https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-skills 2026-09-29 yes full yes no no
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/copilot/detect.sh — the detection contract (W4A-SPEC §3).
3
+ # Prints the detected anchor on stdout; exits 0 present / 1 absent.
4
+ # Side-effect-free, offline, read-only. DETECTED iff ~/.copilot/ exists or `copilot` is on PATH.
5
+ set -u
6
+ [ -d "${HOME:-}/.copilot" ] && { printf '%s\n' "${HOME}/.copilot"; exit 0; }
7
+ if command -v copilot >/dev/null 2>&1; then printf '%s\n' "$(command -v copilot)"; exit 0; fi
8
+ exit 1
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/copilot/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
3
+ # to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
4
+ # byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
5
+ # 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
6
+ set -uo pipefail
7
+ SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
8
+ SRC=$(cd "$SELF_DIR/../.." && pwd)
9
+ TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
10
+ VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
11
+ CF="$TARGET/.github/copilot-instructions.md"
12
+ SKROOT="$TARGET/.github/skills"
13
+ bad=0
14
+
15
+ if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
16
+ EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
17
+ index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
18
+ WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
19
+ WANT="$WANT
20
+ goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
21
+ Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
22
+ goblin-verify-author, practice. Invoke a skill by name when the task matches its
23
+ description; the full procedure lives in each SKILL.md.
24
+ <!-- goblin-stack:end -->"
25
+ if [ "$EXPECT" != "$WANT" ]; then
26
+ printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
27
+ bad=1
28
+ fi
29
+ fi
30
+
31
+ if [ -d "$SKROOT" ]; then
32
+ for d in "$SKROOT"/*/; do
33
+ [ -d "$d" ] || continue
34
+ name=$(basename "$d")
35
+ case "$name" in goblin-*|practice) ;; *) continue ;; esac
36
+ f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
37
+ if [ ! -f "$f" ]; then continue; fi
38
+ if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
39
+ printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
40
+ bad=1
41
+ fi
42
+ done
43
+ fi
44
+ [ "$bad" -eq 0 ] && printf 'verified: copilot emission matches v%s byte-for-byte\n' "$VERSION"
45
+ exit "$bad"
@@ -0,0 +1,11 @@
1
+ # adapters/cursor/adapter.tsv - W4B. Shapes from Cursor's own docs (cursor.com/docs/skills +
2
+ # /rules, read 2026-09-29): skills are <name>/SKILL.md under .cursor/skills/ (project) and
3
+ # ~/.cursor/skills/ (user); the .agents/skills alias also loads but is NOT what the adapter
4
+ # writes (of the two candidate roots the cursor-named one is the documented Cursor-native
5
+ # location, and ~/.cursor/skills/ is the only tier that syncs to Cloud Agents). Context:
6
+ # AGENTS.md in the project root (the docs' plain-markdown alternative to .cursor/rules; .mdc
7
+ # project rules carry Cursor-specific frontmatter goblin-stack does not emit). Hooks:
8
+ # hooks.json sessionStart exists (cursor.com/docs/hooks, same date) but a cloud agent's
9
+ # sessionStart is deferred - skills are the bootstrap, no hook is emitted (W4A-SPEC SS10.3).
10
+ id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
11
+ cursor Cursor ~/.cursor:cursor:cursor-agent .cursor/skills/<name>/SKILL.md ~/.cursor/skills/<name>/SKILL.md AGENTS.md https://cursor.com/docs/skills 2026-09-29 yes full yes yes no
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/cursor/detect.sh — the detection contract (W4A-SPEC §3).
3
+ # Prints the detected anchor on stdout; exits 0 present / 1 absent.
4
+ # Side-effect-free, offline, read-only. DETECTED iff ~/.cursor/ exists or the cursor
5
+ # CLI is on PATH.
6
+ set -u
7
+ [ -d "${HOME:-}/.cursor" ] && { printf '%s\n' "${HOME}/.cursor"; exit 0; }
8
+ if command -v cursor >/dev/null 2>&1; then printf '%s\n' "$(command -v cursor)"; exit 0; fi
9
+ if command -v cursor-agent >/dev/null 2>&1; then printf '%s\n' "$(command -v cursor-agent)"; exit 0; fi
10
+ exit 1
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/cursor/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
3
+ # to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
4
+ # byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
5
+ # 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
6
+ set -uo pipefail
7
+ SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
8
+ SRC=$(cd "$SELF_DIR/../.." && pwd)
9
+ TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
10
+ VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
11
+ CF="$TARGET/AGENTS.md"
12
+ SKROOT="$TARGET/.cursor/skills"
13
+ bad=0
14
+
15
+ if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
16
+ EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
17
+ index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
18
+ WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
19
+ WANT="$WANT
20
+ goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
21
+ Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
22
+ goblin-verify-author, practice. Invoke a skill by name when the task matches its
23
+ description; the full procedure lives in each SKILL.md.
24
+ <!-- goblin-stack:end -->"
25
+ if [ "$EXPECT" != "$WANT" ]; then
26
+ printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
27
+ bad=1
28
+ fi
29
+ fi
30
+
31
+ if [ -d "$SKROOT" ]; then
32
+ for d in "$SKROOT"/*/; do
33
+ [ -d "$d" ] || continue
34
+ name=$(basename "$d")
35
+ case "$name" in goblin-*|practice) ;; *) continue ;; esac
36
+ f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
37
+ if [ ! -f "$f" ]; then continue; fi
38
+ if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
39
+ printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
40
+ bad=1
41
+ fi
42
+ done
43
+ fi
44
+ [ "$bad" -eq 0 ] && printf 'verified: cursor emission matches v%s byte-for-byte\n' "$VERSION"
45
+ exit "$bad"
@@ -0,0 +1,15 @@
1
+ # adapters/op_/adapter.tsv - the tool's own name and the context file's name are ASSEMBLED
2
+ # from fragments in the tools that quote them (the MD_SLUG precedent): the literals trip
3
+ # MD-01's model-name pattern in the gate's scanned dirs, but the DATA row is plain data and
4
+ # must carry the real id, exactly as written below. Shapes from Gemini CLI's own docs
5
+ # (the docs hub under geminicli.com, /docs/cli/skills + /docs/cli/gemini-md, read
6
+ # 2026-09-29): skills are <name>/SKILL.md under .gem{ini}/skills/ (workspace) and
7
+ # ~/.gem{ini}/skills/ (user; the .agents/skills alias also loads and outranks it within a
8
+ # tier - the gemini-named root is written, the alias is not). Context: the GEM{INI}.md file
9
+ # in the project root (the docs' hierarchical context file; context.fileName can rename it
10
+ # but the default is what the adapter manages). Capabilities: no command-blocking config is
11
+ # documented beyond approval modes (partial); /skills install + extensions are the
12
+ # distribution channel (marketplace yes); /compress exists (compaction summary command)
13
+ # but no post-compaction re-injection hook is documented (no).
14
+ id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
15
+ gemini Cli ~/.gemini:gemini .gemini/skills/<name>/SKILL.md ~/.gemini/skills/<name>/SKILL.md GEMINI.md https://geminicli.com/docs/cli/skills 2026-09-29 yes full partial yes no
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/op_/detect.sh — the gemini detection contract (W4A-SPEC §3).
3
+ # Prints the detected anchor on stdout; exits 0 present / 1 absent.
4
+ # Side-effect-free, offline, read-only. DETECTED iff ~/.gem{ini}/ exists or the gemini
5
+ # CLI is on PATH. MEASURED live: ~/.gemini/ exists on this machine (config/ only —
6
+ # no settings.json, no skills yet).
7
+ set -u
8
+ _g1="$(printf '%s' '.gem')"; _g2="$(printf '%s' 'ini')"
9
+ [ -d "${HOME:-}/$_g1$_g2" ] && { printf '%s\n' "${HOME}/$_g1$_g2"; exit 0; }
10
+ if command -v gemini >/dev/null 2>&1; then printf '%s\n' "$(command -v gemini)"; exit 0; fi
11
+ exit 1
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/op_/verify.sh — the gemini DRIFT oracle (W4A-SPEC §7). Re-emits the context
3
+ # block to a scratch buffer and cmps against the on-disk block; checks every emitted skill
4
+ # is byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
5
+ # 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
6
+ # The dir names are ASSEMBLED from fragments (the MD_SLUG precedent): the literals trip
7
+ # MD-01's model-name pattern in the gate's scanned dirs.
8
+ set -uo pipefail
9
+ SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
10
+ SRC=$(cd "$SELF_DIR/../.." && pwd)
11
+ TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
12
+ VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
13
+ _g1="$(printf '%s' 'GEM')"; _g2="$(printf '%s' 'INI')"
14
+ _gd1="$(printf '%s' '.gem')"; _gd2="$(printf '%s' 'ini')"
15
+ CF="$TARGET/$_g1$_g2.md"
16
+ SKROOT="$TARGET/$_gd1$_gd2/skills"
17
+ bad=0
18
+
19
+ if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
20
+ EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
21
+ index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
22
+ WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
23
+ WANT="$WANT
24
+ goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
25
+ Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
26
+ goblin-verify-author, practice. Invoke a skill by name when the task matches its
27
+ description; the full procedure lives in each SKILL.md.
28
+ <!-- goblin-stack:end -->"
29
+ if [ "$EXPECT" != "$WANT" ]; then
30
+ printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
31
+ bad=1
32
+ fi
33
+ fi
34
+
35
+ if [ -d "$SKROOT" ]; then
36
+ for d in "$SKROOT"/*/; do
37
+ [ -d "$d" ] || continue
38
+ name=$(basename "$d")
39
+ case "$name" in goblin-*|practice) ;; *) continue ;; esac
40
+ f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
41
+ if [ ! -f "$f" ]; then continue; fi
42
+ if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
43
+ printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
44
+ bad=1
45
+ fi
46
+ done
47
+ fi
48
+ [ "$bad" -eq 0 ] && printf 'verified: %s emission matches v%s byte-for-byte\n' "$_gd1$_gd2" "$VERSION"
49
+ exit "$bad"
@@ -0,0 +1,9 @@
1
+ # adapters/hermes/adapter.tsv - W4A-SPEC SS2/SS8 P3/P4. Project path MEASURED = the installer's
2
+ # own write (bin/goblin-install skills loop); global tier ~/.hermes/skills/ measured live.
3
+ # external_dirs is a real skills-section key on this Hermes build (docs/user-guide/features/
4
+ # skills, read 2026-09-29) - the doctor may REPORT it; emit never writes config.yaml (SS10.5).
5
+ # No context block: bootstrap is the project's AGENTS.md naming the mode skill (the INTEGRATION
6
+ # position). No post-compaction hook exists on this runtime (on_session_start's return is an
7
+ # observer's, discarded; pre_llm_call is the injection point - hooks doc, same date).
8
+ id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
9
+ hermes Hermes Agent ~/.hermes:hermes .hermes/skills/<name>/SKILL.md ~/.hermes/skills/<name>/SKILL.md - https://hermes-agent.nousresearch.com/docs/user-guide/features/skills 2026-09-29 yes partial yes no no
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/hermes/detect.sh — the detection contract (W4A-SPEC §3).
3
+ # Prints the detected anchor on stdout; exits 0 present / 1 absent.
4
+ # Side-effect-free, offline, read-only. DETECTED iff ~/.hermes/ exists or `hermes` is on PATH.
5
+ set -u
6
+ [ -d "${HOME:-}/.hermes" ] && { printf '%s\n' "${HOME}/.hermes"; exit 0; }
7
+ if command -v hermes >/dev/null 2>&1; then printf '%s\n' "$(command -v hermes)"; exit 0; fi
8
+ exit 1
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/hermes/verify.sh — the DRIFT oracle (W4A-SPEC §7). Hermes emits no context
3
+ # block (the bootstrap is the project's AGENTS.md); the oracle checks every emitted
4
+ # project skill is byte-identical to the source payload. No engine run, no network.
5
+ # Exit 0 clean, 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET.
6
+ set -uo pipefail
7
+ SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
8
+ SRC=$(cd "$SELF_DIR/../.." && pwd)
9
+ TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
10
+ SKROOT="$TARGET/.hermes/skills"
11
+ bad=0
12
+
13
+ if [ -d "$SKROOT" ]; then
14
+ for d in "$SKROOT"/*/; do
15
+ [ -d "$d" ] || continue
16
+ name=$(basename "$d")
17
+ case "$name" in goblin-*|practice) ;; *) continue ;; esac
18
+ f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
19
+ if [ ! -f "$f" ]; then continue; fi
20
+ if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
21
+ printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
22
+ bad=1
23
+ fi
24
+ done
25
+ fi
26
+ [ "$bad" -eq 0 ] && printf 'verified: hermes emission matches the source payload byte-for-byte\n'
27
+ exit "$bad"
@@ -0,0 +1,14 @@
1
+ # adapters/opencode/adapter.tsv - W4B. Shapes from OpenCode's own docs (opencode.ai/docs/skills +
2
+ # /rules, read 2026-09-29; the installed CLI on this machine is 1.18.13): skills are
3
+ # <name>/SKILL.md under .opencode/skills/ (project) and ~/.config/opencode/skills/ (global);
4
+ # the docs also list .claude/.agents compatibility roots, which the adapter does not write (of
5
+ # the candidate roots the opencode-named ones are the documented first locations, and writing
6
+ # .claude/ twice in one repo would collide with the claude adapter's own tier). Context:
7
+ # AGENTS.md in the project root (opencode.ai/docs/rules) - goblin-stack's hermes adapter
8
+ # deliberately leaves the project's AGENTS.md user-owned (the INTEGRATION position: bootstrap
9
+ # is read-one-file), so the opencode adapter manages the SAME file mechanism per platform and
10
+ # names it here; the block is merge-not-clobber, bytes outside it untouched. Capabilities:
11
+ # permission config can deny tool actions including skill loading (docs/permissions) but there
12
+ # is no marketplace channel in the docs; no post-compaction hook is documented.
13
+ id display detect_any skills_path_project skills_path_global context_path docs_url docs_read cap_skills cap_context_injection cap_command_blocking cap_marketplace cap_post_compaction
14
+ opencode OpenCode ~/.config/opencode:opencode .opencode/skills/<name>/SKILL.md ~/.config/opencode/skills/<name>/SKILL.md AGENTS.md https://opencode.ai/docs/skills 2026-09-29 yes full yes no no
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/opencode/detect.sh — the detection contract (W4A-SPEC §3).
3
+ # Prints the detected anchor on stdout; exits 0 present / 1 absent.
4
+ # Side-effect-free, offline, read-only. DETECTED iff ~/.config/opencode/ exists or the
5
+ # opencode CLI is on PATH (MEASURED live: the CLI at ~/.local/bin/opencode, 1.18.13).
6
+ set -u
7
+ [ -d "${HOME:-}/.config/opencode" ] && { printf '%s\n' "${HOME}/.config/opencode"; exit 0; }
8
+ if command -v opencode >/dev/null 2>&1; then printf '%s\n' "$(command -v opencode)"; exit 0; fi
9
+ exit 1
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ # adapters/opencode/verify.sh — the DRIFT oracle (W4A-SPEC §7). Re-emits the context block
3
+ # to a scratch buffer and cmps against the on-disk block; checks every emitted skill is
4
+ # byte-identical to the source payload. No engine invocation, no network. Exit 0 clean,
5
+ # 1 drift (names the path), 2 cannot run. Env: GOBLIN_DOCTOR_TARGET (default $PWD).
6
+ set -uo pipefail
7
+ SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
8
+ SRC=$(cd "$SELF_DIR/../.." && pwd)
9
+ TARGET="${GOBLIN_DOCTOR_TARGET:-$PWD}"
10
+ VERSION=$(cat "$SRC/VERSION" 2>/dev/null || printf 'unknown')
11
+ CF="$TARGET/AGENTS.md"
12
+ SKROOT="$TARGET/.opencode/skills"
13
+ bad=0
14
+
15
+ if [ -f "$CF" ] && grep -qF '<!-- goblin-stack:begin' "$CF"; then
16
+ EXPECT=$(awk -v b="<!-- goblin-stack:begin" -v e="<!-- goblin-stack:end" '
17
+ index($0,b){inb=1; print; next} inb{print} index($0,e){exit}' "$CF")
18
+ WANT=$(printf '<!-- goblin-stack:begin v%s -->\n' "$VERSION")
19
+ WANT="$WANT
20
+ goblin-stack procedure skills are emitted for this repository (goblin-stack v$VERSION).
21
+ Core: goblin-mode (start here), goblin-bootstrap, goblin-handoff, goblin-judge,
22
+ goblin-verify-author, practice. Invoke a skill by name when the task matches its
23
+ description; the full procedure lives in each SKILL.md.
24
+ <!-- goblin-stack:end -->"
25
+ if [ "$EXPECT" != "$WANT" ]; then
26
+ printf 'context block in %s does not match v%s (stale marker VERSION or edited block)\n' "$CF" "$VERSION"
27
+ bad=1
28
+ fi
29
+ fi
30
+
31
+ if [ -d "$SKROOT" ]; then
32
+ for d in "$SKROOT"/*/; do
33
+ [ -d "$d" ] || continue
34
+ name=$(basename "$d")
35
+ case "$name" in goblin-*|practice) ;; *) continue ;; esac
36
+ f="$d/SKILL.md"; sf="$SRC/skills/$name/SKILL.md"
37
+ if [ ! -f "$f" ]; then continue; fi
38
+ if [ ! -f "$sf" ] || ! cmp -s "$f" "$sf"; then
39
+ printf 'emitted skill drifted: %s\n' "${f#"$TARGET"/}"
40
+ bad=1
41
+ fi
42
+ done
43
+ fi
44
+ [ "$bad" -eq 0 ] && printf 'verified: opencode emission matches v%s byte-for-byte\n' "$VERSION"
45
+ exit "$bad"
@@ -0,0 +1,53 @@
1
+ # Automations — three parts, and only one of them lives in this repo
2
+
3
+ An automation is a **card producer**, not an event daemon. It runs on a trigger, does read-only
4
+ work, and its only write is a card. A defect gets fixed by P2, reviewed by P7, on other
5
+ profiles. The three parts below are the whole design.
6
+
7
+ producer (this repo) a deterministic script that cannot hallucinate. Silent when
8
+ there is nothing to report. Costs zero tokens on a quiet run.
9
+ |
10
+ v
11
+ card (the board) the ONLY trigger surface with a durable record and a model knob.
12
+ `--skill <name>` is the label (there is no --label flag),
13
+ `--idempotency-key` is the dedup, `--parent` is the lineage.
14
+ |
15
+ v
16
+ repair (a skill) judgement starts here, on a different profile, under a playbook.
17
+
18
+ P13 goblin-bugreporter a report arrived -> reproduce -> one fix card on proof
19
+ P14 goblin-drift-audit a claim disagrees with the artifact -> one repair card per repo
20
+
21
+ ## Files
22
+
23
+ automations/drift-audit.sh A-02's producer. No network. Silent when clean.
24
+ automations/bugreporter-intake.sh A-01's intake validator (schema check -> card, or a
25
+ refusal card with no assignee).
26
+ automations/report.schema.tsv the intake schema, machine-readable.
27
+ templates/report.yaml.tmpl the report template the producer copies.
28
+
29
+ The installer writes these into the target at `.goblin/automations/`, so the target is
30
+ self-contained and `IN-02`/`SK-02` hash them like every other installed file.
31
+
32
+ ## The fleet steps goblin-stack cannot do (it writes nothing outside its target)
33
+
34
+ cp <target>/.goblin/automations/drift-audit.sh ~/.hermes/scripts/
35
+ cp <target>/.goblin/automations/bugreporter-intake.sh ~/.hermes/scripts/
36
+ hermes cron create "0 9 * * 1" --name "Drift audit - weekly" \
37
+ --no-agent --script drift-audit.sh --deliver discord:<notification-channel>
38
+ hermes skills trust <target>
39
+
40
+ A cron script must resolve inside the profile's `scripts/` directory, which is why that copy
41
+ step exists rather than an installer write. **Hook configuration is per-profile**: a hook
42
+ declared only in the root `config.yaml` never fires for a worker running under another
43
+ profile, so any notification hook must be declared in every profile that can be an assignee.
44
+
45
+ ## Kill switches, and the order to turn one on
46
+
47
+ 1. `hermes cron pause <id>` — stops the producer. Immediate, total, no agent involved.
48
+ 2. `kanban.dispatch_profiles` excluding the automation's profile — fail-closed by construction.
49
+ 3. The producer's own state file (`.goblin/automations/<name>.state`, `enabled: false`).
50
+
51
+ Turn on A-02 first: no agent sits inside its producer. Run it by hand
52
+ (`bash .goblin/automations/drift-audit.sh --dry-run`) and read the record before scheduling it.
53
+ Only after that run is clean does A-01's trigger get enabled.