devrites 1.19.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.
Files changed (232) hide show
  1. package/.claude-plugin/marketplace.json +24 -0
  2. package/.claude-plugin/plugin.json +43 -0
  3. package/CHANGELOG.md +391 -0
  4. package/LICENSE +56 -0
  5. package/NOTICE.md +18 -0
  6. package/README.md +582 -0
  7. package/SECURITY.md +193 -0
  8. package/bin/devrites.mjs +100 -0
  9. package/docs/architecture.md +272 -0
  10. package/docs/cli-mcp.md +57 -0
  11. package/docs/command-map.md +143 -0
  12. package/docs/flow.md +360 -0
  13. package/docs/release.md +29 -0
  14. package/docs/skills.md +214 -0
  15. package/docs/usage.md +325 -0
  16. package/install.sh +359 -0
  17. package/mcp/devrites-mcp.mjs +103 -0
  18. package/pack/.claude/agents/devrites-code-reviewer.md +50 -0
  19. package/pack/.claude/agents/devrites-doubt-reviewer.md +55 -0
  20. package/pack/.claude/agents/devrites-frontend-reviewer.md +52 -0
  21. package/pack/.claude/agents/devrites-performance-reviewer.md +47 -0
  22. package/pack/.claude/agents/devrites-plan-reviewer.md +79 -0
  23. package/pack/.claude/agents/devrites-security-auditor.md +53 -0
  24. package/pack/.claude/agents/devrites-simplifier-reviewer.md +75 -0
  25. package/pack/.claude/agents/devrites-slice-wright.md +181 -0
  26. package/pack/.claude/agents/devrites-spec-reviewer.md +72 -0
  27. package/pack/.claude/agents/devrites-strategy-reviewer.md +62 -0
  28. package/pack/.claude/agents/devrites-test-analyst.md +47 -0
  29. package/pack/.claude/hooks/devrites-a1-guard.sh +81 -0
  30. package/pack/.claude/hooks/devrites-allow.sh +44 -0
  31. package/pack/.claude/hooks/devrites-cursor.sh +28 -0
  32. package/pack/.claude/hooks/devrites-orient.sh +53 -0
  33. package/pack/.claude/hooks/devrites-redwatch.sh +39 -0
  34. package/pack/.claude/hooks/devrites-refresh-indexes.sh +127 -0
  35. package/pack/.claude/hooks/devrites-reviewer-readonly.sh +28 -0
  36. package/pack/.claude/hooks/devrites-statusline.sh +18 -0
  37. package/pack/.claude/hooks/devrites-stop-gate.sh +45 -0
  38. package/pack/.claude/hooks/devrites-wright-scope.sh +35 -0
  39. package/pack/.claude/hooks/hooks.json +52 -0
  40. package/pack/.claude/rules/README.md +48 -0
  41. package/pack/.claude/rules/afk-hitl.md +245 -0
  42. package/pack/.claude/rules/agents.md +98 -0
  43. package/pack/.claude/rules/anti-patterns.md +48 -0
  44. package/pack/.claude/rules/code-review.md +38 -0
  45. package/pack/.claude/rules/coding-style.md +55 -0
  46. package/pack/.claude/rules/context-hygiene.md +97 -0
  47. package/pack/.claude/rules/core.md +119 -0
  48. package/pack/.claude/rules/development-workflow.md +40 -0
  49. package/pack/.claude/rules/documentation.md +27 -0
  50. package/pack/.claude/rules/error-handling.md +33 -0
  51. package/pack/.claude/rules/git-workflow.md +35 -0
  52. package/pack/.claude/rules/hooks.md +38 -0
  53. package/pack/.claude/rules/patterns.md +45 -0
  54. package/pack/.claude/rules/performance.md +27 -0
  55. package/pack/.claude/rules/prose-style.md +101 -0
  56. package/pack/.claude/rules/security.md +63 -0
  57. package/pack/.claude/rules/testing.md +88 -0
  58. package/pack/.claude/rules/tooling.md +72 -0
  59. package/pack/.claude/settings.json +53 -0
  60. package/pack/.claude/skills/devrites-api-interface/SKILL.md +45 -0
  61. package/pack/.claude/skills/devrites-audit/SKILL.md +73 -0
  62. package/pack/.claude/skills/devrites-browser-proof/SKILL.md +38 -0
  63. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +50 -0
  64. package/pack/.claude/skills/devrites-debug-recovery/reference/build-the-loop.md +47 -0
  65. package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +17 -0
  66. package/pack/.claude/skills/devrites-debug-recovery/reference/hypotheses.md +17 -0
  67. package/pack/.claude/skills/devrites-debug-recovery/reference/instrumentation.md +21 -0
  68. package/pack/.claude/skills/devrites-debug-recovery/reference/regression-test.md +31 -0
  69. package/pack/.claude/skills/devrites-doubt/SKILL.md +75 -0
  70. package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +96 -0
  71. package/pack/.claude/skills/devrites-frontend-craft/reference/craft.md +59 -0
  72. package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +116 -0
  73. package/pack/.claude/skills/devrites-frontend-craft/reference/fullstack.md +45 -0
  74. package/pack/.claude/skills/devrites-frontend-craft/reference/quality-standards.md +215 -0
  75. package/pack/.claude/skills/devrites-frontend-craft/reference/reuse-first.md +59 -0
  76. package/pack/.claude/skills/devrites-frontend-craft/reference/shape.md +60 -0
  77. package/pack/.claude/skills/devrites-interview/SKILL.md +81 -0
  78. package/pack/.claude/skills/devrites-lib/SKILL.md +76 -0
  79. package/pack/.claude/skills/devrites-lib/scripts/analyze.sh +78 -0
  80. package/pack/.claude/skills/devrites-lib/scripts/check-acceptance.sh +75 -0
  81. package/pack/.claude/skills/devrites-lib/scripts/close-out.sh +47 -0
  82. package/pack/.claude/skills/devrites-lib/scripts/conventions.py +273 -0
  83. package/pack/.claude/skills/devrites-lib/scripts/coverage.sh +51 -0
  84. package/pack/.claude/skills/devrites-lib/scripts/devrites.sh +69 -0
  85. package/pack/.claude/skills/devrites-lib/scripts/doctor.sh +92 -0
  86. package/pack/.claude/skills/devrites-lib/scripts/evidence-fresh.sh +63 -0
  87. package/pack/.claude/skills/devrites-lib/scripts/footprint.sh +45 -0
  88. package/pack/.claude/skills/devrites-lib/scripts/learnings.sh +74 -0
  89. package/pack/.claude/skills/devrites-lib/scripts/mutation-gate.sh +52 -0
  90. package/pack/.claude/skills/devrites-lib/scripts/package-existence.sh +68 -0
  91. package/pack/.claude/skills/devrites-lib/scripts/preamble.sh +76 -0
  92. package/pack/.claude/skills/devrites-lib/scripts/progress.sh +103 -0
  93. package/pack/.claude/skills/devrites-lib/scripts/readiness.sh +62 -0
  94. package/pack/.claude/skills/devrites-lib/scripts/reconcile.sh +123 -0
  95. package/pack/.claude/skills/devrites-lib/scripts/resolve.sh +279 -0
  96. package/pack/.claude/skills/devrites-lib/scripts/stuck.sh +67 -0
  97. package/pack/.claude/skills/devrites-lib/scripts/test-integrity.sh +87 -0
  98. package/pack/.claude/skills/devrites-lib/scripts/tick-afk.sh +52 -0
  99. package/pack/.claude/skills/devrites-prose-craft/SKILL.md +105 -0
  100. package/pack/.claude/skills/devrites-prose-craft/reference/banned-phrases.md +95 -0
  101. package/pack/.claude/skills/devrites-prose-craft/reference/examples.md +88 -0
  102. package/pack/.claude/skills/devrites-prose-craft/reference/structures.md +134 -0
  103. package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +54 -0
  104. package/pack/.claude/skills/devrites-source-driven/SKILL.md +36 -0
  105. package/pack/.claude/skills/devrites-ux-shape/SKILL.md +121 -0
  106. package/pack/.claude/skills/devrites-ux-shape/reference/brief-template.md +93 -0
  107. package/pack/.claude/skills/devrites-ux-shape/reference/visual-direction-probe.md +48 -0
  108. package/pack/.claude/skills/rite/SKILL.md +135 -0
  109. package/pack/.claude/skills/rite/reference/menu.md +32 -0
  110. package/pack/.claude/skills/rite-adopt/SKILL.md +83 -0
  111. package/pack/.claude/skills/rite-adopt/reference/adoption.md +58 -0
  112. package/pack/.claude/skills/rite-adopt/reference/anti-patterns.md +19 -0
  113. package/pack/.claude/skills/rite-autocomplete/SKILL.md +96 -0
  114. package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +35 -0
  115. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +54 -0
  116. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +59 -0
  117. package/pack/.claude/skills/rite-build/SKILL.md +261 -0
  118. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +145 -0
  119. package/pack/.claude/skills/rite-build/reference/anti-patterns.md +25 -0
  120. package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +149 -0
  121. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +32 -0
  122. package/pack/.claude/skills/rite-build/reference/frontend-trigger.md +39 -0
  123. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +38 -0
  124. package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +43 -0
  125. package/pack/.claude/skills/rite-build/reference/tdd.md +26 -0
  126. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +115 -0
  127. package/pack/.claude/skills/rite-define/SKILL.md +157 -0
  128. package/pack/.claude/skills/rite-define/reference/anti-patterns.md +25 -0
  129. package/pack/.claude/skills/rite-define/reference/gates.md +152 -0
  130. package/pack/.claude/skills/rite-define/reference/plan-template.md +65 -0
  131. package/pack/.claude/skills/rite-doctor/SKILL.md +50 -0
  132. package/pack/.claude/skills/rite-frame/SKILL.md +116 -0
  133. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +68 -0
  134. package/pack/.claude/skills/rite-handoff/SKILL.md +95 -0
  135. package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +34 -0
  136. package/pack/.claude/skills/rite-learn/SKILL.md +82 -0
  137. package/pack/.claude/skills/rite-plan/SKILL.md +82 -0
  138. package/pack/.claude/skills/rite-plan/reference/anti-patterns.md +24 -0
  139. package/pack/.claude/skills/rite-plan/reference/dependency-graph.md +33 -0
  140. package/pack/.claude/skills/rite-plan/reference/replan-and-repair.md +42 -0
  141. package/pack/.claude/skills/rite-plan/reference/slicing.md +52 -0
  142. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +34 -0
  143. package/pack/.claude/skills/rite-polish/SKILL.md +90 -0
  144. package/pack/.claude/skills/rite-polish/reference/anti-ai-slop.md +177 -0
  145. package/pack/.claude/skills/rite-polish/reference/anti-patterns.md +27 -0
  146. package/pack/.claude/skills/rite-polish/reference/backend-polish.md +80 -0
  147. package/pack/.claude/skills/rite-polish/reference/browser-polish-evidence.md +31 -0
  148. package/pack/.claude/skills/rite-polish/reference/code.md +85 -0
  149. package/pack/.claude/skills/rite-polish/reference/design-system-discovery.md +35 -0
  150. package/pack/.claude/skills/rite-polish/reference/harden-checklist.md +109 -0
  151. package/pack/.claude/skills/rite-polish/reference/ui.md +136 -0
  152. package/pack/.claude/skills/rite-pressure-test/SKILL.md +43 -0
  153. package/pack/.claude/skills/rite-prototype/SKILL.md +87 -0
  154. package/pack/.claude/skills/rite-prove/SKILL.md +120 -0
  155. package/pack/.claude/skills/rite-prove/reference/anti-patterns.md +25 -0
  156. package/pack/.claude/skills/rite-prove/reference/browser-proof.md +26 -0
  157. package/pack/.claude/skills/rite-prove/reference/failure-triage.md +25 -0
  158. package/pack/.claude/skills/rite-prove/reference/proof-ladder.md +26 -0
  159. package/pack/.claude/skills/rite-prove/reference/test-command-discovery.md +30 -0
  160. package/pack/.claude/skills/rite-quick/SKILL.md +81 -0
  161. package/pack/.claude/skills/rite-resolve/SKILL.md +113 -0
  162. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +114 -0
  163. package/pack/.claude/skills/rite-review/SKILL.md +170 -0
  164. package/pack/.claude/skills/rite-review/reference/anti-patterns.md +32 -0
  165. package/pack/.claude/skills/rite-review/reference/cognitive-load.md +90 -0
  166. package/pack/.claude/skills/rite-review/reference/feature-scoped-review.md +26 -0
  167. package/pack/.claude/skills/rite-review/reference/five-axis-review.md +46 -0
  168. package/pack/.claude/skills/rite-review/reference/nielsen-heuristics.md +130 -0
  169. package/pack/.claude/skills/rite-review/reference/parallel-dispatch.md +62 -0
  170. package/pack/.claude/skills/rite-review/reference/performance-review.md +28 -0
  171. package/pack/.claude/skills/rite-review/reference/security-review.md +32 -0
  172. package/pack/.claude/skills/rite-seal/SKILL.md +183 -0
  173. package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +27 -0
  174. package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +63 -0
  175. package/pack/.claude/skills/rite-seal/reference/final-evidence.md +72 -0
  176. package/pack/.claude/skills/rite-seal/reference/go-no-go.md +37 -0
  177. package/pack/.claude/skills/rite-seal/reference/parallel-dispatch.md +69 -0
  178. package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +30 -0
  179. package/pack/.claude/skills/rite-seal/reference/seal-template.md +36 -0
  180. package/pack/.claude/skills/rite-ship/SKILL.md +120 -0
  181. package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +25 -0
  182. package/pack/.claude/skills/rite-ship/reference/close-out.md +31 -0
  183. package/pack/.claude/skills/rite-ship/reference/design-memory.md +120 -0
  184. package/pack/.claude/skills/rite-ship/reference/git-ship.md +42 -0
  185. package/pack/.claude/skills/rite-ship/reference/ship-template.md +33 -0
  186. package/pack/.claude/skills/rite-spec/SKILL.md +126 -0
  187. package/pack/.claude/skills/rite-spec/reference/acceptance-criteria.md +31 -0
  188. package/pack/.claude/skills/rite-spec/reference/anti-patterns.md +25 -0
  189. package/pack/.claude/skills/rite-spec/reference/interview-patterns.md +56 -0
  190. package/pack/.claude/skills/rite-spec/reference/investigation.md +64 -0
  191. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +61 -0
  192. package/pack/.claude/skills/rite-spec/reference/references-intake.md +57 -0
  193. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +73 -0
  194. package/pack/.claude/skills/rite-spec/reference/spec-template.md +124 -0
  195. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +159 -0
  196. package/pack/.claude/skills/rite-status/SKILL.md +101 -0
  197. package/pack/.claude/skills/rite-temper/SKILL.md +119 -0
  198. package/pack/.claude/skills/rite-temper/reference/anti-patterns.md +29 -0
  199. package/pack/.claude/skills/rite-temper/reference/review-dimensions.md +65 -0
  200. package/pack/.claude/skills/rite-temper/reference/scope-modes.md +53 -0
  201. package/pack/.claude/skills/rite-temper/reference/significance.md +46 -0
  202. package/pack/.claude/skills/rite-temper/reference/strategy-template.md +90 -0
  203. package/pack/.claude/skills/rite-vet/SKILL.md +155 -0
  204. package/pack/.claude/skills/rite-vet/reference/anti-patterns.md +29 -0
  205. package/pack/.claude/skills/rite-vet/reference/artifacts.md +135 -0
  206. package/pack/.claude/skills/rite-vet/reference/cross-model.md +41 -0
  207. package/pack/.claude/skills/rite-vet/reference/depth.md +53 -0
  208. package/pack/.claude/skills/rite-vet/reference/eng-lenses.md +48 -0
  209. package/pack/.claude/skills/rite-vet/reference/review-axes.md +167 -0
  210. package/pack/.claude/skills/rite-zoom-out/SKILL.md +75 -0
  211. package/package.json +68 -0
  212. package/scripts/build-release-tarball.sh +74 -0
  213. package/scripts/check-cross-refs.py +121 -0
  214. package/scripts/check-no-global-writes.sh +44 -0
  215. package/scripts/check-rule-uniqueness.sh +73 -0
  216. package/scripts/devrites-detect.sh +175 -0
  217. package/scripts/eval-runner.py +273 -0
  218. package/scripts/grade-feature.sh +104 -0
  219. package/scripts/install-lib.sh +83 -0
  220. package/scripts/pin.sh +166 -0
  221. package/scripts/render-eval-summary.py +48 -0
  222. package/scripts/run-evals.sh +149 -0
  223. package/scripts/run-outcome-evals.sh +49 -0
  224. package/scripts/scan-pack-security.py +209 -0
  225. package/scripts/scan-supply-chain-iocs.py +127 -0
  226. package/scripts/supply-chain-iocs.json +11 -0
  227. package/scripts/sync-version.sh +56 -0
  228. package/scripts/validate-frontmatter.py +149 -0
  229. package/scripts/validate-workflow-security.py +86 -0
  230. package/scripts/validate.sh +234 -0
  231. package/uninstall.sh +137 -0
  232. package/update.sh +196 -0
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env bash
2
+ # DevRites progress footer — render the active feature's position deterministically.
3
+ # Run LAST (output step) by every workspace-operating rite-* skill via the standard
4
+ # resolution snippet, the mirror of preamble.sh (which runs first). Read-only; never
5
+ # mutates workspace files.
6
+ #
7
+ # Prints three lines of "chrome" the skill's own fact lines sit under:
8
+ # ── rite-<phase> ───────────────────────────
9
+ # Slice <built>/<total> ██████░░░░ <last-built name> ✓ (or "✅ ALL BUILT" at N/N)
10
+ # Flow spec ✓ define ✓ build ◉ prove ○ … ship ○
11
+ #
12
+ # The slice meter answers "how many steps left" (built/total); the ✅ ALL BUILT marker
13
+ # + the `build ✓` ribbon glyph answer "is the build phase complete". The skill prints the
14
+ # what-was-done / next-step / hygiene lines beneath; this script owns only the visuals so
15
+ # every rite-* footer looks the same with zero model drift.
16
+ #
17
+ # Usage: progress.sh [slug]
18
+ # slug — optional override; defaults to .devrites/ACTIVE
19
+ #
20
+ # Paths resolve relative to CWD (the project root), like preamble.sh. Prints nothing and
21
+ # exits 0 when there is no active workspace, so pre-spec callers stay silent.
22
+
23
+ set -u
24
+ slug="${1:-$(cat .devrites/ACTIVE 2>/dev/null)}"
25
+ d=".devrites/work/$slug"
26
+ s="$d/state.md"
27
+
28
+ [ -n "$slug" ] && [ -f "$s" ] || exit 0
29
+
30
+ # --- phase ----------------------------------------------------------------------------
31
+ # First whitespace token after "Phase:" (ignores any trailing " # comment").
32
+ phase="$(awk -F': *' '/^- Phase:/{print $2; exit}' "$s" | awk '{print $1}')"
33
+ [ -n "$phase" ] || phase="spec"
34
+
35
+ # --- slice tally ----------------------------------------------------------------------
36
+ # total / built counts + the name of the last built slice, parsed from "## Slice progress".
37
+ read -r total built lastbuilt <<EOF
38
+ $(awk '
39
+ /^## Slice progress/ { inp=1; next }
40
+ inp && /^## / { inp=0 }
41
+ inp && /^- \[/ {
42
+ total++
43
+ name=$0
44
+ sub(/^- \[[^]]*\][[:space:]]*/, "", name) # drop "- [x] "
45
+ sub(/^Slice[[:space:]]*[0-9]+:[[:space:]]*/, "", name) # drop "Slice N: "
46
+ sub(/[[:space:]]+(built|pending)[[:space:]]*$/, "", name) # drop trailing "built|pending"
47
+ sub(/[[:space:]]+[^[:alnum:][:space:]]+[[:space:]]*$/, "", name) # drop the " — " separator
48
+ if ($0 ~ /\[[xX]\]/) { built++; last=name }
49
+ }
50
+ END { printf "%d %d %s", total+0, built+0, last }
51
+ ' "$s")
52
+ EOF
53
+ total="${total:-0}"; built="${built:-0}"
54
+
55
+ # --- header rule ----------------------------------------------------------------------
56
+ title="rite-${phase}"
57
+ header="── ${title} "
58
+ while [ "${#header}" -lt 44 ]; do header="${header}─"; done
59
+ printf '%s\n' "$header"
60
+
61
+ # --- slice meter ----------------------------------------------------------------------
62
+ # Bar is 10 cells; filled = round(built/total*10). Skipped entirely before any slices exist.
63
+ if [ "$total" -gt 0 ]; then
64
+ filled=$(( (built * 10 + total / 2) / total ))
65
+ [ "$filled" -gt 10 ] && filled=10
66
+ bar=""; i=0
67
+ while [ "$i" -lt "$filled" ]; do bar="${bar}█"; i=$((i+1)); done
68
+ while [ "$i" -lt 10 ]; do bar="${bar}░"; i=$((i+1)); done
69
+ if [ "$built" -ge "$total" ]; then
70
+ printf 'Slices %d/%d %s ✅ ALL BUILT\n' "$built" "$total" "$bar"
71
+ else
72
+ tail=""
73
+ [ -n "$lastbuilt" ] && tail=" ${lastbuilt} ✓"
74
+ printf 'Slice %d/%d %s%s\n' "$built" "$total" "$bar" "$tail"
75
+ fi
76
+ fi
77
+
78
+ # --- flow ribbon ----------------------------------------------------------------------
79
+ # Lifecycle spine; optional temper/vet appear only once their artifact exists.
80
+ order=(spec)
81
+ [ -f "$d/strategy.md" ] && order+=(temper)
82
+ order+=(define)
83
+ [ -f "$d/eng-review.md" ] && order+=(vet)
84
+ order+=(build prove polish review seal ship)
85
+
86
+ # Current index in the spine. `plan` is replan → sits at build; `done` is past ship.
87
+ cur="$phase"; [ "$cur" = "plan" ] && cur="build"
88
+ idx=-1
89
+ if [ "$phase" = "done" ]; then
90
+ idx=${#order[@]}
91
+ else
92
+ for n in "${!order[@]}"; do [ "${order[$n]}" = "$cur" ] && { idx=$n; break; }; done
93
+ fi
94
+
95
+ ribbon="Flow "
96
+ for n in "${!order[@]}"; do
97
+ if [ "$n" -lt "$idx" ]; then g="✓"
98
+ elif [ "$n" -eq "$idx" ]; then g="◉"
99
+ else g="○"
100
+ fi
101
+ ribbon="${ribbon} ${order[$n]} ${g}"
102
+ done
103
+ printf '%s\n' "$ribbon"
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env bash
2
+ # Build-readiness gate for DevRites — turns /rite-build's step-0 prose checks
3
+ # ("if Status is awaiting_human → STOP", "if no Plan approved → STOP") into a
4
+ # deterministic exit code, the same way tick-afk.sh enforces the AFK cap. A gate
5
+ # the model *is* stopped by beats a gate the model is *asked* to honor.
6
+ # Read-only; never mutates the workspace.
7
+ #
8
+ # Usage: readiness.sh [slug]
9
+ # slug — optional; defaults to .devrites/ACTIVE
10
+ #
11
+ # Checks .devrites/work/<slug>/state.md, in order:
12
+ # - workspace + state.md exist
13
+ # - Status != awaiting_human (a HITL gate is open)
14
+ # - Status != blocked
15
+ # - "Plan approved:" is set (/rite-define writes it when the human confirms)
16
+ #
17
+ # Exit codes:
18
+ # 0 ready to build
19
+ # 2 plan not approved → /rite-define (then confirm the plan)
20
+ # 3 awaiting human → /rite-resolve <qid> "<answer>"
21
+ # 4 blocked → /rite-plan repair | unblock
22
+ # 5 no workspace / state.md → /rite-spec <feature>
23
+
24
+ set -euo pipefail
25
+
26
+ slug="${1:-$(cat .devrites/ACTIVE 2>/dev/null || true)}"
27
+ d=".devrites/work/$slug"
28
+ s="$d/state.md"
29
+ if [ -z "$slug" ] || [ ! -f "$s" ]; then
30
+ printf 'readiness: no active workspace/state.md (slug=%s) — run /rite-spec <feature>\n' "${slug:-<unset>}" >&2
31
+ exit 5
32
+ fi
33
+
34
+ # Read a state.md field. Tolerates the markdown-bullet rendering ("- Key: val")
35
+ # and a trailing " | none" / "# comment".
36
+ field() {
37
+ awk -v k="$1" 'match($0, "^[[:space:]]*-?[[:space:]]*" k ":[[:space:]]*") {
38
+ rest = substr($0, RLENGTH + 1)
39
+ sub(/[[:space:]]*(#|\|).*$/, "", rest)
40
+ sub(/[[:space:]]+$/, "", rest)
41
+ print rest; exit
42
+ }' "$s"
43
+ }
44
+
45
+ status="$(field Status)"
46
+ case "$status" in
47
+ awaiting_human)
48
+ printf 'readiness: STOP — Status: awaiting_human. Resume with /rite-resolve <qid> "<answer>".\n' >&2
49
+ exit 3 ;;
50
+ blocked)
51
+ printf 'readiness: STOP — Status: blocked. Repair with /rite-plan repair (or unblock).\n' >&2
52
+ exit 4 ;;
53
+ esac
54
+
55
+ approved="$(field 'Plan approved')"
56
+ if [ -z "$approved" ] || [ "$approved" = "none" ]; then
57
+ printf 'readiness: STOP — plan not approved (state.md has no "Plan approved"). Run /rite-define.\n' >&2
58
+ exit 2
59
+ fi
60
+
61
+ printf 'readiness: OK — plan approved %s, status %s. Ready to build.\n' "$approved" "${status:-running}"
62
+ exit 0
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env bash
2
+ # Reconcile gate for /rite-build — proves the orchestrator never edited source
3
+ # itself. The slice-wright (a dispatched subagent) is the ONLY writer of code +
4
+ # tests; the orchestrator writes bookkeeping under .devrites/ and nothing else.
5
+ # This turns that rule (A1: "the orchestrator never edits source") into an exit
6
+ # code instead of a prose promise the model can rationalize past — same doctrine
7
+ # as readiness.sh / tick-afk.sh. Read-only w.r.t. the user's git index.
8
+ #
9
+ # Two phases, both called by /rite-build:
10
+ # snapshot — step 3, right BEFORE dispatching the wright. Captures the working
11
+ # tree (tracked + untracked, .gitignore honored) as a git tree
12
+ # object, without touching the user's real index/staging. The base
13
+ # file doubles as the "mid-build window" marker: while it exists the
14
+ # optional pre-block hook (devrites-a1-guard.sh) is armed; a clean
15
+ # check removes it (a violation keeps it hot until re-dispatch).
16
+ # check — step 6 (record), AFTER the wright returns and the orchestrator has
17
+ # written the wright's reported `Files changed` paths (one per line)
18
+ # to .devrites/work/<slug>/.reconcile-claimed. Diffs the tree against
19
+ # the snapshot; every changed path that is NOT under .devrites/ and
20
+ # NOT in the claimed manifest was edited outside the wright -> STOP.
21
+ #
22
+ # Usage:
23
+ # reconcile.sh snapshot [slug]
24
+ # reconcile.sh check [slug]
25
+ # slug — optional; defaults to .devrites/ACTIVE
26
+ #
27
+ # Exit codes:
28
+ # 0 ok — clean (check), snapshot written, or gate skipped (non-git repo /
29
+ # inline fallback) with a printed notice
30
+ # 5 VIOLATION — source changed outside the wright's claimed set: STOP. A1
31
+ # breach; re-dispatch the wright (continue it once) or revert the edits
32
+ # 6 setup error — bad args, missing snapshot, or missing claimed manifest
33
+
34
+ set -euo pipefail
35
+
36
+ mode="${1:-}"
37
+ slug="${2:-$(cat .devrites/ACTIVE 2>/dev/null || true)}"
38
+
39
+ # Non-git project: nothing to diff against. Skip rather than block the build.
40
+ root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
41
+ if [ -z "$root" ]; then
42
+ printf 'reconcile: not a git repo — gate skipped, verify the diff by hand.\n' >&2
43
+ exit 0
44
+ fi
45
+
46
+ d=".devrites/work/$slug"
47
+ base="$d/.reconcile-base"
48
+ claimed="$d/.reconcile-claimed"
49
+ inline="$d/.reconcile-inline"
50
+
51
+ if [ -z "$slug" ] || [ ! -d "$d" ]; then
52
+ printf 'reconcile: no active workspace (slug=%s) — nothing to reconcile.\n' "${slug:-<unset>}" >&2
53
+ exit 6
54
+ fi
55
+
56
+ # Capture the whole working tree (tracked + untracked, .gitignore respected) as a
57
+ # tree object via a throwaway index, so the user's real index/staging is never
58
+ # touched. Prints the tree sha on stdout.
59
+ worktree_tree() {
60
+ local idx="${TMPDIR:-/tmp}/devrites-reconcile-$$.idx"
61
+ rm -f "$idx"
62
+ GIT_INDEX_FILE="$idx" git -C "$root" add -A >/dev/null 2>&1
63
+ local tree; tree="$(GIT_INDEX_FILE="$idx" git -C "$root" write-tree)"
64
+ rm -f "$idx"
65
+ printf '%s\n' "$tree"
66
+ }
67
+
68
+ # Close the mid-build window: drop the base (disarms the a1-guard hook) and the
69
+ # per-slice manifest/sentinel. Called only when the slice is accepted clean or
70
+ # was the inline fallback — a violation deliberately leaves the window hot.
71
+ close_window() { rm -f "$base" "$claimed" "$inline"; }
72
+
73
+ case "$mode" in
74
+ snapshot)
75
+ rm -f "$inline" "$claimed" # clear any stale sentinel / prior-slice manifest
76
+ worktree_tree > "$base" # base present == mid-build window open
77
+ printf 'reconcile: snapshot captured for %s.\n' "$slug"
78
+ exit 0 ;;
79
+ check)
80
+ # Inline fallback: the orchestrator legitimately wrote the code itself
81
+ # (no wright was dispatched), so the gate does not apply.
82
+ if [ -f "$inline" ]; then
83
+ printf 'reconcile: inline-fallback run (no wright dispatched) — gate skipped.\n' >&2
84
+ close_window
85
+ exit 0
86
+ fi
87
+ if [ ! -f "$base" ]; then
88
+ printf 'reconcile: no snapshot (.reconcile-base) — run "reconcile.sh snapshot" before dispatch.\n' >&2
89
+ exit 6
90
+ fi
91
+ if [ ! -f "$claimed" ]; then
92
+ printf 'reconcile: no claimed manifest (.reconcile-claimed) — write the wright Files-changed paths first.\n' >&2
93
+ exit 6
94
+ fi
95
+ base_tree="$(cat "$base")"
96
+ now_tree="$(worktree_tree)"
97
+ # Every path changed since the snapshot, minus bookkeeping (.devrites/ is the
98
+ # orchestrator's own legit write target). Anything left that the wright did
99
+ # not claim was edited by someone other than the wright.
100
+ viol=0
101
+ violist=""
102
+ while IFS= read -r f; do
103
+ [ -z "$f" ] && continue
104
+ case "$f" in .devrites/*) continue ;; esac
105
+ if ! grep -qxF "$f" "$claimed"; then
106
+ viol=$(( viol + 1 ))
107
+ violist="${violist} - ${f}
108
+ "
109
+ fi
110
+ done < <(git -C "$root" diff --name-only "$base_tree" "$now_tree")
111
+ if [ "$viol" -gt 0 ]; then
112
+ printf 'reconcile: STOP — %d source file(s) changed OUTSIDE the wright (A1 breach):\n' "$viol" >&2
113
+ printf '%s' "$violist" >&2
114
+ printf 'The orchestrator must NOT edit source. Re-dispatch the wright (continue it once) or revert these.\n' >&2
115
+ exit 5
116
+ fi
117
+ close_window
118
+ printf "reconcile: OK — every source change is the wright's; orchestrator touched only bookkeeping.\n"
119
+ exit 0 ;;
120
+ *)
121
+ printf 'reconcile: usage: reconcile.sh <snapshot|check> [slug]\n' >&2
122
+ exit 6 ;;
123
+ esac
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env bash
2
+ # Resolve an open question in the active DevRites workspace.
3
+ # Used by /rite-resolve. Keeps questions.md and state.md consistent.
4
+ #
5
+ # Usage:
6
+ # resolve.sh <qid> "<answer>"
7
+ # resolve.sh --drop <qid> ["<reason>"]
8
+ # resolve.sh --batch <path-to-yaml>
9
+ # resolve.sh next-qid <questions.md path>
10
+ #
11
+ # Exit codes:
12
+ # 0 resolved
13
+ # 2 no active workspace
14
+ # 3 qid not found
15
+ # 4 qid not open (already answered/dropped)
16
+ # 5 bad arguments
17
+ # 6 qid collision (next-qid: computed id already has a header)
18
+
19
+ set -u
20
+
21
+ die() { printf '%s\n' "$*" >&2; exit "${2:-1}"; }
22
+
23
+ # next-qid: print the next sequential qid for TODAY. Counts existing
24
+ # `## q-YYYY-MM-DD-` headers for today's date, prints q-YYYY-MM-DD-NNN with the
25
+ # next zero-padded id, and refuses (exit 6) if that header already exists.
26
+ # Operates on an explicit questions.md path — no active workspace required.
27
+ if [ "${1:-}" = "next-qid" ]; then
28
+ qpath="${2:-}"
29
+ [ -n "$qpath" ] || die "Usage: resolve.sh next-qid <questions.md path>" 5
30
+ today="$(date +%F)"
31
+ # A fresh questions.md may not exist yet — treat absent as zero headers.
32
+ if [ -f "$qpath" ]; then
33
+ used="$(grep -c "^## q-${today}-" "$qpath" 2>/dev/null || true)"
34
+ else
35
+ used=0
36
+ fi
37
+ next=$(( used + 1 ))
38
+ qid="$(printf 'q-%s-%03d' "$today" "$next")"
39
+ # Defend against gaps/manual edits: never hand out an id that already exists.
40
+ if [ -f "$qpath" ] && grep -q "^## ${qid}\([[:space:]]\|$\)" "$qpath"; then
41
+ die "qid already present: $qid (questions.md edited out of sequence)" 6
42
+ fi
43
+ printf '%s\n' "$qid"
44
+ exit 0
45
+ fi
46
+
47
+ slug="$(cat .devrites/ACTIVE 2>/dev/null || true)"
48
+ [ -n "$slug" ] || die "No active workspace. Run /rite-spec <feature> first." 2
49
+ work=".devrites/work/$slug"
50
+ qfile="$work/questions.md"
51
+ sfile="$work/state.md"
52
+ [ -f "$qfile" ] || die "questions.md missing at $qfile" 2
53
+ [ -f "$sfile" ] || die "state.md missing at $sfile" 2
54
+
55
+ mode=""
56
+ qid=""
57
+ payload=""
58
+ case "${1:-}" in
59
+ --drop)
60
+ mode="drop"
61
+ qid="${2:-}"
62
+ payload="${3:-dropped}"
63
+ ;;
64
+ --batch)
65
+ mode="batch"
66
+ payload="${2:-}"
67
+ [ -f "$payload" ] || die "Batch file not found: $payload" 5
68
+ ;;
69
+ "")
70
+ die "Usage: resolve.sh <qid> \"<answer>\" | --drop <qid> [\"<reason>\"] | --batch <file>" 5
71
+ ;;
72
+ *)
73
+ mode="answer"
74
+ qid="$1"
75
+ payload="${2:-}"
76
+ [ -n "$payload" ] || die "Answer text required for $qid" 5
77
+ ;;
78
+ esac
79
+
80
+ now() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
81
+
82
+ # Apply a single resolution to questions.md. Awk rewrites the file atomically.
83
+ apply_one() {
84
+ local _qid="$1" _status="$2" _answer="$3"
85
+ local _now; _now="$(now)"
86
+
87
+ awk -v qid="$_qid" -v st="$_status" -v ans="$_answer" -v ts="$_now" '
88
+ BEGIN { in_q=0; touched=0; found=0; status_seen=0 }
89
+ function flush_if_target() {
90
+ # called when leaving the target block
91
+ if (in_q && !status_seen) {
92
+ # malformed entry without status — append the closing fields
93
+ print "status: " st
94
+ print "answered_at: " ts
95
+ print "answer: " ans
96
+ }
97
+ }
98
+ /^## q-/ {
99
+ flush_if_target()
100
+ in_q = ($0 ~ ("^## " qid "([[:space:]]|$)"))
101
+ status_seen = 0
102
+ if (in_q) { found = 1 }
103
+ print
104
+ next
105
+ }
106
+ in_q && /^status:/ {
107
+ status_seen = 1
108
+ cur = $0; sub(/^status:[[:space:]]*/, "", cur)
109
+ if (cur != "open") {
110
+ # signal non-open with sentinel to stderr-equivalent: write file unchanged
111
+ # we cannot abort from awk cleanly; mark and continue
112
+ print "status: " cur
113
+ in_q = 0
114
+ bad_state = 1
115
+ next
116
+ }
117
+ print "status: " st
118
+ next
119
+ }
120
+ in_q && /^answered_at:/ {
121
+ print "answered_at: " ts
122
+ next
123
+ }
124
+ in_q && /^answer:/ {
125
+ print "answer: " ans
126
+ next
127
+ }
128
+ { print }
129
+ END {
130
+ flush_if_target()
131
+ if (!found) exit 10
132
+ if (bad_state) exit 11
133
+ }
134
+ ' "$qfile" > "$qfile.tmp"
135
+ rc=$?
136
+
137
+ if [ "$rc" -eq 10 ]; then
138
+ rm -f "$qfile.tmp"
139
+ die "qid not found: $_qid" 3
140
+ fi
141
+ if [ "$rc" -eq 11 ]; then
142
+ rm -f "$qfile.tmp"
143
+ die "qid not open (already answered/dropped): $_qid" 4
144
+ fi
145
+
146
+ # If the awk output is missing answered_at / answer (entry without those keys),
147
+ # append them at the end of the block by re-running with insertion mode.
148
+ mv "$qfile.tmp" "$qfile"
149
+
150
+ # Ensure answered_at + answer keys exist for the target qid (insert after `status:` if missing)
151
+ awk -v qid="$_qid" -v ts="$_now" -v ans="$_answer" '
152
+ BEGIN { in_q=0; saw_at=0; saw_ans=0; saw_status_line=0 }
153
+ /^## q-/ {
154
+ if (in_q) {
155
+ if (!saw_at) print "answered_at: " ts
156
+ if (!saw_ans) print "answer: " ans
157
+ }
158
+ in_q = ($0 ~ ("^## " qid "([[:space:]]|$)"))
159
+ saw_at = 0; saw_ans = 0; saw_status_line = 0
160
+ print; next
161
+ }
162
+ in_q && /^answered_at:/ { saw_at = 1 }
163
+ in_q && /^answer:/ { saw_ans = 1 }
164
+ in_q && /^status:/ { saw_status_line = 1 }
165
+ { print }
166
+ END {
167
+ if (in_q) {
168
+ if (!saw_at) print "answered_at: " ts
169
+ if (!saw_ans) print "answer: " ans
170
+ }
171
+ }
172
+ ' "$qfile" > "$qfile.tmp" && mv "$qfile.tmp" "$qfile"
173
+ }
174
+
175
+ # Update state.md: if the Awaiting human block references the qid, remove the block,
176
+ # flip Status: awaiting_human → running, clear Next step, and append a Log entry.
177
+ # Two-pass: scan to detect a match, then rewrite the file with the side-effects applied.
178
+ clear_state_if_matches() {
179
+ local _qid="$1"
180
+ local _now; _now="$(now)"
181
+
182
+ # Pass 1: does the Awaiting block reference this qid?
183
+ local matched
184
+ matched="$(awk -v qid="$_qid" '
185
+ BEGIN { in_aw=0; m=0 }
186
+ /^## Awaiting human/ { in_aw=1; next }
187
+ in_aw && /^##[[:space:]]/ { in_aw=0 }
188
+ in_aw {
189
+ if ($0 ~ ("qid:[[:space:]]*" qid "([[:space:]]|$)")) m=1
190
+ }
191
+ END { print m }
192
+ ' "$sfile")"
193
+
194
+ if [ "$matched" != "1" ]; then
195
+ return 0
196
+ fi
197
+
198
+ # Pass 2: rewrite. Drop the Awaiting human block (header + body until the next `## `),
199
+ # flip Status, clear Next step, append a Log entry under ## Log.
200
+ awk -v qid="$_qid" -v ts="$_now" '
201
+ BEGIN { in_aw=0; in_log=0; log_appended=0 }
202
+ /^## Awaiting human/ { in_aw=1; next }
203
+ in_aw && /^##[[:space:]]/ { in_aw=0 } # next ## header ends the block
204
+ in_aw { next } # swallow lines inside the awaiting block
205
+
206
+ /^- Status:/ {
207
+ print "- Status: running"
208
+ next
209
+ }
210
+ /^- Next step:/ {
211
+ print "- Next step: (resume — `/rite-build` to continue the workflow)"
212
+ next
213
+ }
214
+ /^## Log/ {
215
+ print
216
+ in_log = 1
217
+ next
218
+ }
219
+ in_log && /^##[[:space:]]/ {
220
+ # leaving the log section — append our entry before this header if we have not yet
221
+ if (!log_appended) {
222
+ printf "- %s build: resolved %s\n", ts, qid
223
+ log_appended = 1
224
+ }
225
+ in_log = 0
226
+ print
227
+ next
228
+ }
229
+ { print }
230
+
231
+ END {
232
+ if (in_log && !log_appended) {
233
+ printf "- %s build: resolved %s\n", ts, qid
234
+ }
235
+ }
236
+ ' "$sfile" > "$sfile.tmp" && mv "$sfile.tmp" "$sfile"
237
+ }
238
+
239
+ case "$mode" in
240
+ answer)
241
+ apply_one "$qid" "answered" "$payload"
242
+ clear_state_if_matches "$qid"
243
+ printf 'Resolved: %s\n' "$qid"
244
+ printf 'Status: answered\n'
245
+ printf 'Workspace: questions.md + state.md updated.\n'
246
+ ;;
247
+ drop)
248
+ apply_one "$qid" "dropped" "$payload"
249
+ clear_state_if_matches "$qid"
250
+ printf 'Dropped: %s\n' "$qid"
251
+ printf 'Reason: %s\n' "$payload"
252
+ printf 'Workspace: questions.md + state.md updated.\n'
253
+ ;;
254
+ batch)
255
+ # Batch format: each line is `qid: <answer>` or `--drop qid: <reason>`. See answer-protocol.md.
256
+ while IFS= read -r line; do
257
+ case "$line" in
258
+ ''|\#*) continue ;;
259
+ --drop*)
260
+ rest="${line#--drop }"
261
+ bid="${rest%%:*}"
262
+ reason="${rest#*:}"
263
+ reason="${reason# }"
264
+ apply_one "$bid" "dropped" "$reason"
265
+ clear_state_if_matches "$bid"
266
+ printf 'Dropped: %s — %s\n' "$bid" "$reason"
267
+ ;;
268
+ *)
269
+ bid="${line%%:*}"
270
+ ans="${line#*:}"
271
+ ans="${ans# }"
272
+ apply_one "$bid" "answered" "$ans"
273
+ clear_state_if_matches "$bid"
274
+ printf 'Resolved: %s\n' "$bid"
275
+ ;;
276
+ esac
277
+ done < "$payload"
278
+ ;;
279
+ esac
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bash
2
+ # stuck.sh — loop detector for the unattended build. The slice-wright or orchestrator
3
+ # appends one record per tool action; `check` flags the semantic-repeat patterns that
4
+ # mean the agent is stuck burning tokens (the #1 silent AFK failure mode): the same
5
+ # action repeating, an action<->error ping-pong, or one action dominating the window.
6
+ # A detected loop pauses the build even under AFK (rules/afk-hitl.md). Deterministic;
7
+ # matching is by (tool,target) hash, timestamps ignored.
8
+ #
9
+ # stuck.sh log <slug> <tool> [target] # append one action record (caller)
10
+ # stuck.sh check <slug> [window] # non-zero if the recent window looks stuck
11
+ #
12
+ # The log lives at .devrites/work/<slug>/action.log as "<epoch> <tool> <sha1>" lines.
13
+ #
14
+ # Exit codes:
15
+ # 0 ok — not stuck (or logging, or no workspace: never fail the caller on log)
16
+ # 2 bad args
17
+ # 3 STUCK — the recent window is a loop: stop and escalate to a blocking question
18
+ set -u
19
+
20
+ cmd="${1:-}"; slug="${2:-}"
21
+ [ -n "$cmd" ] && [ -n "$slug" ] || { echo "usage: stuck.sh log|check <slug> [...]" >&2; exit 2; }
22
+
23
+ dir=".devrites/work/$slug"
24
+ log="$dir/action.log"
25
+
26
+ hash_of() {
27
+ if command -v sha1sum >/dev/null 2>&1; then
28
+ printf '%s' "$1" | sha1sum | awk '{print $1}'
29
+ else
30
+ printf '%s' "$1" | shasum | awk '{print $1}'
31
+ fi
32
+ }
33
+
34
+ case "$cmd" in
35
+ log)
36
+ tool="${3:-}"; target="${4:-}"
37
+ [ -n "$tool" ] || { echo "usage: stuck.sh log <slug> <tool> [target]" >&2; exit 2; }
38
+ [ -d "$dir" ] || exit 0
39
+ printf '%s %s %s\n' "$(date +%s)" "$tool" "$(hash_of "$tool|$target")" >> "$log"
40
+ ;;
41
+ check)
42
+ win="${3:-4}"
43
+ [ -f "$log" ] || { echo "stuck: no actions logged — not stuck."; exit 0; }
44
+ awk -v win="$win" '
45
+ { h[NR]=$3 }
46
+ END {
47
+ if (NR < win) { print "stuck: only " NR " action(s) — not stuck."; exit 0 }
48
+ same=1
49
+ for (i=NR-win+1; i<=NR; i++) if (h[i]!=h[NR]) { same=0; break }
50
+ if (same) { print "stuck: last " win " actions identical — STUCK."; exit 3 }
51
+ if (NR>=6) {
52
+ pp=1
53
+ for (i=NR-5; i<=NR; i++) { e=(((NR-i)%2)==0)?h[NR]:h[NR-1]; if (h[i]!=e) { pp=0; break } }
54
+ if (pp && h[NR]!=h[NR-1]) { print "stuck: A<->B ping-pong over last 6 — STUCK."; exit 3 }
55
+ }
56
+ if (NR>=8) {
57
+ for (i=NR-7;i<=NR;i++) c[h[i]]++
58
+ top=0; for (k in c) if (c[k]>top) top=c[k]
59
+ if (top>=6) { print "stuck: one action " top "/8 of recent window — STUCK."; exit 3 }
60
+ }
61
+ print "stuck: recent window varied — not stuck."
62
+ }
63
+ ' "$log"
64
+ ;;
65
+ *)
66
+ echo "usage: stuck.sh log|check <slug> [...]" >&2; exit 2 ;;
67
+ esac