devrites 3.2.5 → 3.2.7

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 (36) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +1 -1
  3. package/docs/cli.md +0 -1
  4. package/docs/command-map.md +1 -1
  5. package/docs/orchestration.md +1 -2
  6. package/engine/commands.go +1 -1
  7. package/engine/hooks_agent_dispatch.go +98 -33
  8. package/engine/internal/lib/reconcile.go +1 -1
  9. package/engine/internal/lib/reconcile_test.go +4 -12
  10. package/engine/internal/lib/util.go +2 -2
  11. package/engine/main.go +0 -3
  12. package/engine/root_routing_test.go +0 -1
  13. package/engine/tests/hook_test.go +235 -0
  14. package/pack/.claude/skills/rite-build/reference/forge.md +2 -2
  15. package/pack/.claude/skills/rite-build/reference/phase-contract.md +11 -20
  16. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +2 -5
  17. package/pack/.claude/skills/rite-prove/reference/acceptance-proof.md +0 -1
  18. package/pack/.claude/skills/rite-seal/reference/final-evidence.md +0 -1
  19. package/pack/generated/claude/skills/rite-build/reference/forge.md +2 -2
  20. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +11 -20
  21. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +2 -5
  22. package/pack/generated/claude/skills/rite-prove/reference/acceptance-proof.md +0 -1
  23. package/pack/generated/claude/skills/rite-seal/reference/final-evidence.md +0 -1
  24. package/pack/generated/codex/skills/rite-build/reference/forge.md +2 -2
  25. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +11 -20
  26. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +2 -5
  27. package/pack/generated/codex/skills/rite-prove/reference/acceptance-proof.md +0 -1
  28. package/pack/generated/codex/skills/rite-seal/reference/final-evidence.md +0 -1
  29. package/package.json +1 -1
  30. package/scripts/build-release-tarball.sh +1 -0
  31. package/scripts/validate.sh +0 -1
  32. package/engine/internal/lib/packageexistence.go +0 -968
  33. package/engine/internal/lib/packageexistence_test.go +0 -506
  34. package/engine/testdata/golden/TestParityPackageExistence/declared.golden +0 -2
  35. package/engine/testdata/golden/TestParityPackageExistence/default-import-subpath.golden +0 -2
  36. package/engine/tests/parity_packageexistence_test.go +0 -64
@@ -899,6 +899,18 @@ func writeCodexSkillContract(t *testing.T, root, skill, roles string) {
899
899
  }
900
900
  }
901
901
 
902
+ func writeCodexConditionalSkillContract(t *testing.T, root, skill, role string) {
903
+ t.Helper()
904
+ dir := filepath.Join(filepath.Dir(root), ".agents", "skills", skill)
905
+ if err := os.MkdirAll(dir, 0o755); err != nil {
906
+ t.Fatal(err)
907
+ }
908
+ contract := "---\nname: " + skill + "\ndescription: Test skill.\nrequired-agent-roles: none\n---\n\nConditionally dispatch `" + role + "` when needed.\n"
909
+ if err := os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte(contract), 0o600); err != nil {
910
+ t.Fatal(err)
911
+ }
912
+ }
913
+
902
914
  func writeCodexV2Rollouts(
903
915
  t *testing.T,
904
916
  codeHome, projectRoot, sessionID, turnID, role, taskName string,
@@ -1096,6 +1108,156 @@ func TestCodexAgentDispatchArmsInstalledSkillRequirements(t *testing.T) {
1096
1108
  }
1097
1109
  }
1098
1110
 
1111
+ func TestCodexAgentDispatchPromptGivesExactNamedCallBeforeCompletion(t *testing.T) {
1112
+ root := newWorkspace(t)
1113
+ role := "devrites-plan-drafter"
1114
+ writeCodexAgentContract(t, root, role)
1115
+ writeCodexSkillContract(t, root, "rite-plan", role)
1116
+
1117
+ out, stderr, code := runDevritesIO(t, root, hookPayload(t, map[string]any{
1118
+ "hook_event_name": "UserPromptSubmit",
1119
+ "session_id": "session-plan-drafter-guidance",
1120
+ "turn_id": "turn-plan-drafter-guidance",
1121
+ "prompt": "$rite-plan repair",
1122
+ }), nil, "hook", "agent-dispatch", "--harness=codex")
1123
+ if code != 0 {
1124
+ t.Fatalf("arm exit=%d stderr=%s", code, stderr)
1125
+ }
1126
+ for _, want := range []string{
1127
+ "MANDATORY DISPATCH THIS TURN",
1128
+ "spawn_agent",
1129
+ "agent_type=" + role,
1130
+ "unique task_name",
1131
+ `fork_turns="none"`,
1132
+ ".codex/agents/" + role + ".toml",
1133
+ "send agent_type anyway",
1134
+ "Wait for the returned child",
1135
+ } {
1136
+ if !strings.Contains(out, want) {
1137
+ t.Fatalf("initial dispatch guidance omitted %q:\n%s", want, out)
1138
+ }
1139
+ }
1140
+ }
1141
+
1142
+ func TestCodexConditionalAgentDispatchRejectsDefaultAndArmsNamedRole(t *testing.T) {
1143
+ root := newWorkspace(t)
1144
+ role := "devrites-proof-runner"
1145
+ writeCodexAgentContract(t, root, role)
1146
+ writeCodexConditionalSkillContract(t, root, "rite-polish", role)
1147
+ sessionID, turnID := "session-conditional-role", "turn-conditional-role"
1148
+
1149
+ out, stderr, code := runDevritesIO(t, root, hookPayload(t, map[string]any{
1150
+ "hook_event_name": "UserPromptSubmit",
1151
+ "session_id": sessionID,
1152
+ "turn_id": turnID,
1153
+ "prompt": "$rite-polish",
1154
+ }), nil, "hook", "agent-dispatch", "--harness=codex")
1155
+ if code != 0 {
1156
+ t.Fatalf("arm exit=%d stderr=%s", code, stderr)
1157
+ }
1158
+ for _, want := range []string{
1159
+ "CONDITIONAL DISPATCH RULE",
1160
+ "exact named agent_type=devrites-<role>",
1161
+ `fork_turns="none"`,
1162
+ "send agent_type anyway",
1163
+ "never use a default child",
1164
+ } {
1165
+ if !strings.Contains(out, want) {
1166
+ t.Fatalf("conditional dispatch guidance omitted %q:\n%s", want, out)
1167
+ }
1168
+ }
1169
+
1170
+ for _, agentType := range []string{"", "default"} {
1171
+ out, stderr, code = runDevritesIO(t, root, hookPayload(t, map[string]any{
1172
+ "hook_event_name": "PreToolUse",
1173
+ "session_id": sessionID,
1174
+ "turn_id": turnID,
1175
+ "tool_name": "spawn_agent",
1176
+ "tool_use_id": "default-" + agentType,
1177
+ "tool_input": map[string]any{
1178
+ "agent_type": agentType,
1179
+ "task_name": "conditional_default",
1180
+ "fork_turns": "none",
1181
+ "message": "Run the conditional DevRites check.",
1182
+ },
1183
+ }), nil, "hook", "agent-dispatch", "--harness=codex")
1184
+ if code != 0 {
1185
+ t.Fatalf("default spawn exit=%d stderr=%s", code, stderr)
1186
+ }
1187
+ if decision, reason := parsePermissionDecision(t, out); decision != "deny" ||
1188
+ !strings.Contains(reason, "exact named agent_type=devrites-<role>") {
1189
+ t.Fatalf("conditional default child not denied: decision=%q reason=%q out=%s", decision, reason, out)
1190
+ }
1191
+ }
1192
+
1193
+ out, stderr, code = runDevritesIO(t, root, hookPayload(t, map[string]any{
1194
+ "hook_event_name": "PreToolUse",
1195
+ "session_id": sessionID,
1196
+ "turn_id": turnID,
1197
+ "tool_name": "spawn_agent",
1198
+ "tool_use_id": "missing-named-type",
1199
+ "tool_input": map[string]any{
1200
+ "task_name": "conditional_missing_type",
1201
+ "fork_turns": "none",
1202
+ "message": "Read .codex/agents/" + role + ".toml and run the check.",
1203
+ },
1204
+ }), nil, "hook", "agent-dispatch", "--harness=codex")
1205
+ if code != 0 {
1206
+ t.Fatalf("missing type spawn exit=%d stderr=%s", code, stderr)
1207
+ }
1208
+ if decision, reason := parsePermissionDecision(t, out); decision != "deny" ||
1209
+ !strings.Contains(reason, "agent_type="+role) ||
1210
+ !strings.Contains(reason, "send agent_type anyway") {
1211
+ t.Fatalf("missing named type did not get exact retry: decision=%q reason=%q out=%s", decision, reason, out)
1212
+ }
1213
+
1214
+ out, stderr, code = runDevritesIO(t, root, hookPayload(t, map[string]any{
1215
+ "hook_event_name": "PreToolUse",
1216
+ "session_id": sessionID,
1217
+ "turn_id": turnID,
1218
+ "tool_name": "spawn_agent",
1219
+ "tool_use_id": "named-conditional",
1220
+ "tool_input": map[string]any{
1221
+ "agent_type": role,
1222
+ "task_name": "conditional_proof",
1223
+ "fork_turns": "none",
1224
+ "message": "Run the conditional DevRites check.",
1225
+ },
1226
+ }), nil, "hook", "agent-dispatch", "--harness=codex")
1227
+ if code != 0 || strings.TrimSpace(out) != "" {
1228
+ t.Fatalf("named conditional spawn rejected: exit=%d out=%s stderr=%s", code, out, stderr)
1229
+ }
1230
+
1231
+ out, stderr, code = runDevritesIO(t, root, hookPayload(t, map[string]any{
1232
+ "hook_event_name": "Stop",
1233
+ "session_id": sessionID,
1234
+ "turn_id": turnID,
1235
+ }), nil, "hook", "agent-dispatch", "--harness=codex")
1236
+ if code != 0 || !strings.Contains(out, "DevRites dispatch for "+role+" is not complete") {
1237
+ t.Fatalf("named conditional spawn was not armed: exit=%d out=%s stderr=%s", code, out, stderr)
1238
+ }
1239
+ }
1240
+
1241
+ func TestCodexAgentDispatchAllowsDefaultChildOutsideSkillTurn(t *testing.T) {
1242
+ root := newWorkspace(t)
1243
+ out, stderr, code := runDevritesIO(t, root, hookPayload(t, map[string]any{
1244
+ "hook_event_name": "PreToolUse",
1245
+ "session_id": "session-unrelated-default",
1246
+ "turn_id": "turn-unrelated-default",
1247
+ "tool_name": "spawn_agent",
1248
+ "tool_use_id": "unrelated-default",
1249
+ "tool_input": map[string]any{
1250
+ "agent_type": "default",
1251
+ "task_name": "unrelated",
1252
+ "fork_turns": "all",
1253
+ "message": "Handle an unrelated bounded task.",
1254
+ },
1255
+ }), nil, "hook", "agent-dispatch", "--harness=codex")
1256
+ if code != 0 || strings.TrimSpace(out) != "" {
1257
+ t.Fatalf("unrelated default child was rejected: exit=%d out=%s stderr=%s", code, out, stderr)
1258
+ }
1259
+ }
1260
+
1099
1261
  func TestCodexAgentDispatchRejectsInvalidInstalledSkillRequirements(t *testing.T) {
1100
1262
  root := newWorkspace(t)
1101
1263
  writeCodexSkillContract(t, root, "rite-vet", "root-inline-reviewer")
@@ -1406,6 +1568,79 @@ func TestCodexAgentDispatchConfirmsDurableV2NamedRole(t *testing.T) {
1406
1568
  }
1407
1569
  }
1408
1570
 
1571
+ func TestCodexAgentDispatchConfirmsConditionalDurableV2NamedRole(t *testing.T) {
1572
+ root := newWorkspace(t)
1573
+ codeHome := t.TempDir()
1574
+ sessionID, turnID := "session-v2-conditional", "turn-v2-conditional"
1575
+ role := "devrites-security-auditor"
1576
+ writeCodexAgentContract(t, root, role)
1577
+ writeCodexConditionalSkillContract(t, root, "devrites-audit", role)
1578
+
1579
+ runDevritesIO(t, root, hookPayload(t, map[string]any{
1580
+ "hook_event_name": "UserPromptSubmit",
1581
+ "session_id": sessionID,
1582
+ "turn_id": turnID,
1583
+ "prompt": "$devrites-audit security",
1584
+ }), nil, "hook", "agent-dispatch", "--harness=codex")
1585
+
1586
+ writeCodexV2Rollouts(
1587
+ t, codeHome, filepath.Dir(root), sessionID, turnID, role,
1588
+ "devrites_security_auditor_conditional", true, true, true,
1589
+ )
1590
+ stop := hookPayload(t, map[string]any{
1591
+ "hook_event_name": "Stop",
1592
+ "session_id": sessionID,
1593
+ "turn_id": turnID,
1594
+ })
1595
+ if out, stderr, code := runDevritesIO(t, root, stop,
1596
+ []string{"CODEX_HOME=" + codeHome},
1597
+ "hook", "agent-dispatch", "--harness=codex"); code != 0 || strings.TrimSpace(out) != "" {
1598
+ t.Fatalf("conditional durable v2 stop exit=%d out=%s stderr=%s", code, out, stderr)
1599
+ }
1600
+ }
1601
+
1602
+ func TestCodexAgentDispatchRejectsConditionalDurableV2DefaultChild(t *testing.T) {
1603
+ root := newWorkspace(t)
1604
+ codeHome := t.TempDir()
1605
+ sessionID, turnID := "session-v2-default", "turn-v2-default"
1606
+ writeCodexConditionalSkillContract(t, root, "devrites-audit", "devrites-security-auditor")
1607
+
1608
+ runDevritesIO(t, root, hookPayload(t, map[string]any{
1609
+ "hook_event_name": "UserPromptSubmit",
1610
+ "session_id": sessionID,
1611
+ "turn_id": turnID,
1612
+ "prompt": "$devrites-audit security",
1613
+ }), nil, "hook", "agent-dispatch", "--harness=codex")
1614
+
1615
+ writeCodexV2Rollouts(
1616
+ t, codeHome, filepath.Dir(root), sessionID, turnID, "default",
1617
+ "default_conditional_child", true, true, true,
1618
+ )
1619
+ stop := hookPayload(t, map[string]any{
1620
+ "hook_event_name": "Stop",
1621
+ "session_id": sessionID,
1622
+ "turn_id": turnID,
1623
+ })
1624
+ out, stderr, code := runDevritesIO(t, root, stop,
1625
+ []string{"CODEX_HOME=" + codeHome},
1626
+ "hook", "agent-dispatch", "--harness=codex")
1627
+ if code != 0 {
1628
+ t.Fatalf("conditional default stop exit=%d stderr=%s", code, stderr)
1629
+ }
1630
+ var decision struct {
1631
+ Decision string `json:"decision"`
1632
+ Reason string `json:"reason"`
1633
+ }
1634
+ if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &decision); err != nil {
1635
+ t.Fatalf("invalid conditional default response: %v\n%s", err, out)
1636
+ }
1637
+ if decision.Decision != "block" ||
1638
+ !strings.Contains(decision.Reason, "default or non-DevRites child") ||
1639
+ !strings.Contains(decision.Reason, "never use a default child") {
1640
+ t.Fatalf("conditional V2 default child passed: %#v", decision)
1641
+ }
1642
+ }
1643
+
1409
1644
  func TestCodexAgentDispatchRejectsIncompleteDurableV2(t *testing.T) {
1410
1645
  for _, tc := range []struct {
1411
1646
  name string
@@ -166,8 +166,8 @@ decision.
166
166
 
167
167
  Hand the winner's structured artifact to the normal cycle. Run immediate
168
168
  reconciliation, doubt every stood decision, and complete test-integrity,
169
- package-existence, targeted, browser, and other slice proof. Only after every
170
- required gate is green:
169
+ targeted, browser, and other slice proof. Only after every required gate is
170
+ green:
171
171
 
172
172
  ```bash
173
173
  devrites-engine forge record "$RUN_ID" verification verified \
@@ -73,14 +73,13 @@ See also [`one-slice-cycle.md`](one-slice-cycle.md).
73
73
  (reported in its `Principles` field, or that you detect against `.devrites/principles.md`) is
74
74
  handled here like an irreversible-risk item: block, route to a human-approved scoped
75
75
  exception in the register or stop; never doubt-and-accept a principle violation into the slice.
76
- 5. **Run retained-baseline integrity gates, then recover any objective red.**
76
+ 5. **Run the retained-baseline integrity gate, then recover any objective red.**
77
77
  ```bash
78
78
  devrites-engine test-integrity; echo "test-integrity rc=$?"
79
- devrites-engine package-existence; echo "package-existence rc=$?"
80
79
  ```
81
- If the wright's `Gates` were red (targeted tests / types / lint), either integrity gate
82
- failed, or it
83
- couldn't verify: do **not** mark the slice `built`, and **do not fix the code yourself**.
80
+ If the wright's `Gates` were red (targeted tests / types / lint), `test-integrity`
81
+ failed, or proof could not be verified: do **not** mark the slice `built`, and **do not
82
+ fix the code yourself**.
84
83
  Classify each causal fingerprint through
85
84
  [`cleanup-and-classify.md`](../../devrites-debug-recovery/reference/cleanup-and-classify.md),
86
85
  run `devrites-engine recovery route <class>`, and follow the `recovery-route/v1`
@@ -95,9 +94,9 @@ See also [`one-slice-cycle.md`](one-slice-cycle.md).
95
94
  `recovery clear --class <class> "<root cause>" <slug>` only after green. Before every
96
95
  re-dispatch, update the root-owned allowlist only for an accepted in-slice path and run
97
96
  `reconcile snapshot`: after a clean check this refreshes canonical-state scope while
98
- retaining the original source baseline. Repeat reconcile and both integrity gates on every
99
- return. This owns red
100
- gates, missing coverage, browser/runtime failures, and workflow-tool defects.
97
+ retaining the original source baseline. Repeat reconcile and the integrity gate on every
98
+ return. This owns red gates, missing coverage, browser/runtime failures, and
99
+ workflow-tool defects.
101
100
  After recovery:
102
101
  - green → continue to record;
103
102
  - product-contract/acceptance ambiguity or irreversible risk → open the genuine human gate
@@ -106,22 +105,14 @@ See also [`one-slice-cycle.md`](one-slice-cycle.md).
106
105
  - exhausted objective failure → preserve reproduction/dead ends, set `Status: blocked` and
107
106
  `Next step: /rite-plan unblock`, then STOP without a question or `/rite-resolve`.
108
107
  6. **Close the retained baseline, then record. You are the canonical writer.**
109
- Reconciliation ran immediately on return and the two integrity gates ran in step 5.
108
+ Reconciliation ran immediately on return and `test-integrity` ran in step 5.
110
109
  **Exit 3 → hard STOP:** a test was deleted, skipped, or de-asserted since the slice base: the
111
110
  slice went green by weakening its tests, a Critical protocol violation. Revert the weakening and
112
111
  re-dispatch the wright; do **not** mark the slice `built`.
113
112
 
114
- The package-existence gate (anti-hallucination) requires every new third-party import
115
- to appear in a project manifest:
116
- **Exit 3 → STOP:** an imported package is not declared in any manifest (`package.json`, `go.mod`,
117
- `requirements.txt`, `pyproject.toml`, `Pipfile`, `Cargo.toml`): the classic shape of a
118
- hallucinated or typo-squatted dependency. Confirm the name on the registry and declare it via the
119
- package manager, or remove the import; do **not** mark the slice `built`. The gate is deterministic
120
- and fail-open (not a git repo / no manifest / stdlib-only import → rc 0). If the nearest
121
- manifest declares the package, treat the mismatch as a workflow-tool defect under bounded
122
- recovery, not a plan repair or retry-authorization question. Once every gate and doubt
123
- verdict is accepted, a forged slice records successful verification and runs
124
- manifest-only cleanup per [`forge.md`](forge.md). Then close the private window:
113
+ Once every gate and doubt verdict is accepted, a forged slice records successful
114
+ verification and runs manifest-only cleanup per [`forge.md`](forge.md). Then close the
115
+ private window:
125
116
  ```bash
126
117
  devrites-engine reconcile close
127
118
  ```
@@ -66,15 +66,12 @@ and exact changed-file set. Before the root writes any canonical record, run:
66
66
  ```bash
67
67
  devrites-engine reconcile check; echo "reconcile rc=$?"
68
68
  devrites-engine test-integrity; echo "test-integrity rc=$?"
69
- devrites-engine package-existence; echo "package-existence rc=$?"
70
69
  ```
71
70
 
72
71
  - Reconcile `5`: reject the result. Preserve pre-snapshot user work; restore only the
73
72
  unauthorized slice delta; never widen scope from the writer's self-report.
74
73
  - Test integrity `3`: a test was deleted, muted, focused, or de-asserted. Treat it as a
75
74
  Critical protocol failure and correct it through the wright.
76
- - Package existence `3`: verify the exact package/version and fix the bounded workflow
77
- or dependency defect through recovery. It is not a reason to ask for retry approval.
78
75
  - A setup/corrupt-baseline error blocks acceptance; never fall back silently to `HEAD`.
79
76
 
80
77
  The clean reconcile check records that source has not changed since inspection. The
@@ -105,13 +102,13 @@ Before every retry:
105
102
 
106
103
  That refresh requires the prior clean check, re-fingerprints root-owned state, and keeps
107
104
  the original source baseline. Re-dispatch with the exact output, attempt count, and dead
108
- ends. Then repeat reconcile and both integrity gates from zero. Clear recovery only after
105
+ ends. Then repeat reconcile and `test-integrity` from zero. Clear recovery only after
109
106
  green with `recovery clear --class <class> "<root cause>" <slug>`. An exhausted objective
110
107
  failure becomes a technical blocker with its reproduction, not a `rite-resolve` question.
111
108
 
112
109
  ## Close and record
113
110
 
114
- When reconciliation, integrity, package, proof, and doubt gates all pass:
111
+ When reconciliation, integrity, proof, and doubt gates all pass:
115
112
 
116
113
  ```bash
117
114
  devrites-engine reconcile close
@@ -21,7 +21,6 @@ mutation tool when present. Run:
21
21
  ```bash
22
22
  devrites-engine test-integrity
23
23
  devrites-engine mutation-gate
24
- devrites-engine package-existence
25
24
  ```
26
25
 
27
26
  A test that stays green on broken code is unproven. Pure transforms also get a
@@ -53,7 +53,6 @@ gates:
53
53
  ```bash
54
54
  devrites-engine test-integrity; echo "test-integrity rc=$?" # exit 3 = a test deleted/skipped/loosened → Critical NO-GO
55
55
  devrites-engine mutation-gate # changed-files mutation score — band the verdict; survivors are unproven behaviours
56
- devrites-engine package-existence; echo "package-existence rc=$?" # exit 3 = a new import isn't declared in any manifest (hallucinated/typo-squatted dep) → Critical NO-GO
57
56
  ```
58
57
  A test weakened since the slice base is a **Critical NO-GO**: the suite went green by lowering the
59
58
  bar, not by the code being right. Record the mutation score in `seal.md`; under
@@ -166,8 +166,8 @@ decision.
166
166
 
167
167
  Hand the winner's structured artifact to the normal cycle. Run immediate
168
168
  reconciliation, doubt every stood decision, and complete test-integrity,
169
- package-existence, targeted, browser, and other slice proof. Only after every
170
- required gate is green:
169
+ targeted, browser, and other slice proof. Only after every required gate is
170
+ green:
171
171
 
172
172
  ```bash
173
173
  devrites-engine forge record "$RUN_ID" verification verified \
@@ -73,14 +73,13 @@ See also [`one-slice-cycle.md`](one-slice-cycle.md).
73
73
  (reported in its `Principles` field, or that you detect against `.devrites/principles.md`) is
74
74
  handled here like an irreversible-risk item: block, route to a human-approved scoped
75
75
  exception in the register or stop; never doubt-and-accept a principle violation into the slice.
76
- 5. **Run retained-baseline integrity gates, then recover any objective red.**
76
+ 5. **Run the retained-baseline integrity gate, then recover any objective red.**
77
77
  ```bash
78
78
  devrites-engine test-integrity; echo "test-integrity rc=$?"
79
- devrites-engine package-existence; echo "package-existence rc=$?"
80
79
  ```
81
- If the wright's `Gates` were red (targeted tests / types / lint), either integrity gate
82
- failed, or it
83
- couldn't verify: do **not** mark the slice `built`, and **do not fix the code yourself**.
80
+ If the wright's `Gates` were red (targeted tests / types / lint), `test-integrity`
81
+ failed, or proof could not be verified: do **not** mark the slice `built`, and **do not
82
+ fix the code yourself**.
84
83
  Classify each causal fingerprint through
85
84
  [`cleanup-and-classify.md`](../../devrites-debug-recovery/reference/cleanup-and-classify.md),
86
85
  run `devrites-engine recovery route <class>`, and follow the `recovery-route/v1`
@@ -95,9 +94,9 @@ See also [`one-slice-cycle.md`](one-slice-cycle.md).
95
94
  `recovery clear --class <class> "<root cause>" <slug>` only after green. Before every
96
95
  re-dispatch, update the root-owned allowlist only for an accepted in-slice path and run
97
96
  `reconcile snapshot`: after a clean check this refreshes canonical-state scope while
98
- retaining the original source baseline. Repeat reconcile and both integrity gates on every
99
- return. This owns red
100
- gates, missing coverage, browser/runtime failures, and workflow-tool defects.
97
+ retaining the original source baseline. Repeat reconcile and the integrity gate on every
98
+ return. This owns red gates, missing coverage, browser/runtime failures, and
99
+ workflow-tool defects.
101
100
  After recovery:
102
101
  - green → continue to record;
103
102
  - product-contract/acceptance ambiguity or irreversible risk → open the genuine human gate
@@ -106,22 +105,14 @@ See also [`one-slice-cycle.md`](one-slice-cycle.md).
106
105
  - exhausted objective failure → preserve reproduction/dead ends, set `Status: blocked` and
107
106
  `Next step: /rite-plan unblock`, then STOP without a question or `/rite-resolve`.
108
107
  6. **Close the retained baseline, then record. You are the canonical writer.**
109
- Reconciliation ran immediately on return and the two integrity gates ran in step 5.
108
+ Reconciliation ran immediately on return and `test-integrity` ran in step 5.
110
109
  **Exit 3 → hard STOP:** a test was deleted, skipped, or de-asserted since the slice base: the
111
110
  slice went green by weakening its tests, a Critical protocol violation. Revert the weakening and
112
111
  re-dispatch the wright; do **not** mark the slice `built`.
113
112
 
114
- The package-existence gate (anti-hallucination) requires every new third-party import
115
- to appear in a project manifest:
116
- **Exit 3 → STOP:** an imported package is not declared in any manifest (`package.json`, `go.mod`,
117
- `requirements.txt`, `pyproject.toml`, `Pipfile`, `Cargo.toml`): the classic shape of a
118
- hallucinated or typo-squatted dependency. Confirm the name on the registry and declare it via the
119
- package manager, or remove the import; do **not** mark the slice `built`. The gate is deterministic
120
- and fail-open (not a git repo / no manifest / stdlib-only import → rc 0). If the nearest
121
- manifest declares the package, treat the mismatch as a workflow-tool defect under bounded
122
- recovery, not a plan repair or retry-authorization question. Once every gate and doubt
123
- verdict is accepted, a forged slice records successful verification and runs
124
- manifest-only cleanup per [`forge.md`](forge.md). Then close the private window:
113
+ Once every gate and doubt verdict is accepted, a forged slice records successful
114
+ verification and runs manifest-only cleanup per [`forge.md`](forge.md). Then close the
115
+ private window:
125
116
  ```bash
126
117
  devrites-engine reconcile close
127
118
  ```
@@ -66,15 +66,12 @@ and exact changed-file set. Before the root writes any canonical record, run:
66
66
  ```bash
67
67
  devrites-engine reconcile check; echo "reconcile rc=$?"
68
68
  devrites-engine test-integrity; echo "test-integrity rc=$?"
69
- devrites-engine package-existence; echo "package-existence rc=$?"
70
69
  ```
71
70
 
72
71
  - Reconcile `5`: reject the result. Preserve pre-snapshot user work; restore only the
73
72
  unauthorized slice delta; never widen scope from the writer's self-report.
74
73
  - Test integrity `3`: a test was deleted, muted, focused, or de-asserted. Treat it as a
75
74
  Critical protocol failure and correct it through the wright.
76
- - Package existence `3`: verify the exact package/version and fix the bounded workflow
77
- or dependency defect through recovery. It is not a reason to ask for retry approval.
78
75
  - A setup/corrupt-baseline error blocks acceptance; never fall back silently to `HEAD`.
79
76
 
80
77
  The clean reconcile check records that source has not changed since inspection. The
@@ -105,13 +102,13 @@ Before every retry:
105
102
 
106
103
  That refresh requires the prior clean check, re-fingerprints root-owned state, and keeps
107
104
  the original source baseline. Re-dispatch with the exact output, attempt count, and dead
108
- ends. Then repeat reconcile and both integrity gates from zero. Clear recovery only after
105
+ ends. Then repeat reconcile and `test-integrity` from zero. Clear recovery only after
109
106
  green with `recovery clear --class <class> "<root cause>" <slug>`. An exhausted objective
110
107
  failure becomes a technical blocker with its reproduction, not a `rite-resolve` question.
111
108
 
112
109
  ## Close and record
113
110
 
114
- When reconciliation, integrity, package, proof, and doubt gates all pass:
111
+ When reconciliation, integrity, proof, and doubt gates all pass:
115
112
 
116
113
  ```bash
117
114
  devrites-engine reconcile close
@@ -21,7 +21,6 @@ mutation tool when present. Run:
21
21
  ```bash
22
22
  devrites-engine test-integrity
23
23
  devrites-engine mutation-gate
24
- devrites-engine package-existence
25
24
  ```
26
25
 
27
26
  A test that stays green on broken code is unproven. Pure transforms also get a
@@ -53,7 +53,6 @@ gates:
53
53
  ```bash
54
54
  devrites-engine test-integrity; echo "test-integrity rc=$?" # exit 3 = a test deleted/skipped/loosened → Critical NO-GO
55
55
  devrites-engine mutation-gate # changed-files mutation score — band the verdict; survivors are unproven behaviours
56
- devrites-engine package-existence; echo "package-existence rc=$?" # exit 3 = a new import isn't declared in any manifest (hallucinated/typo-squatted dep) → Critical NO-GO
57
56
  ```
58
57
  A test weakened since the slice base is a **Critical NO-GO**: the suite went green by lowering the
59
58
  bar, not by the code being right. Record the mutation score in `seal.md`; under
@@ -166,8 +166,8 @@ decision.
166
166
 
167
167
  Hand the winner's structured artifact to the normal cycle. Run immediate
168
168
  reconciliation, doubt every stood decision, and complete test-integrity,
169
- package-existence, targeted, browser, and other slice proof. Only after every
170
- required gate is green:
169
+ targeted, browser, and other slice proof. Only after every required gate is
170
+ green:
171
171
 
172
172
  ```bash
173
173
  devrites-engine forge record "$RUN_ID" verification verified \
@@ -73,14 +73,13 @@ See also [`one-slice-cycle.md`](one-slice-cycle.md).
73
73
  (reported in its `Principles` field, or that you detect against `.devrites/principles.md`) is
74
74
  handled here like an irreversible-risk item: block, route to a human-approved scoped
75
75
  exception in the register or stop; never doubt-and-accept a principle violation into the slice.
76
- 5. **Run retained-baseline integrity gates, then recover any objective red.**
76
+ 5. **Run the retained-baseline integrity gate, then recover any objective red.**
77
77
  ```bash
78
78
  devrites-engine test-integrity; echo "test-integrity rc=$?"
79
- devrites-engine package-existence; echo "package-existence rc=$?"
80
79
  ```
81
- If the wright's `Gates` were red (targeted tests / types / lint), either integrity gate
82
- failed, or it
83
- couldn't verify: do **not** mark the slice `built`, and **do not fix the code yourself**.
80
+ If the wright's `Gates` were red (targeted tests / types / lint), `test-integrity`
81
+ failed, or proof could not be verified: do **not** mark the slice `built`, and **do not
82
+ fix the code yourself**.
84
83
  Classify each causal fingerprint through
85
84
  [`cleanup-and-classify.md`](../../devrites-debug-recovery/reference/cleanup-and-classify.md),
86
85
  run `devrites-engine recovery route <class>`, and follow the `recovery-route/v1`
@@ -95,9 +94,9 @@ See also [`one-slice-cycle.md`](one-slice-cycle.md).
95
94
  `recovery clear --class <class> "<root cause>" <slug>` only after green. Before every
96
95
  re-dispatch, update the root-owned allowlist only for an accepted in-slice path and run
97
96
  `reconcile snapshot`: after a clean check this refreshes canonical-state scope while
98
- retaining the original source baseline. Repeat reconcile and both integrity gates on every
99
- return. This owns red
100
- gates, missing coverage, browser/runtime failures, and workflow-tool defects.
97
+ retaining the original source baseline. Repeat reconcile and the integrity gate on every
98
+ return. This owns red gates, missing coverage, browser/runtime failures, and
99
+ workflow-tool defects.
101
100
  After recovery:
102
101
  - green → continue to record;
103
102
  - product-contract/acceptance ambiguity or irreversible risk → open the genuine human gate
@@ -106,22 +105,14 @@ See also [`one-slice-cycle.md`](one-slice-cycle.md).
106
105
  - exhausted objective failure → preserve reproduction/dead ends, set `Status: blocked` and
107
106
  `Next step: $rite-plan unblock`, then STOP without a question or `$rite-resolve`.
108
107
  6. **Close the retained baseline, then record. You are the canonical writer.**
109
- Reconciliation ran immediately on return and the two integrity gates ran in step 5.
108
+ Reconciliation ran immediately on return and `test-integrity` ran in step 5.
110
109
  **Exit 3 → hard STOP:** a test was deleted, skipped, or de-asserted since the slice base: the
111
110
  slice went green by weakening its tests, a Critical protocol violation. Revert the weakening and
112
111
  re-dispatch the wright; do **not** mark the slice `built`.
113
112
 
114
- The package-existence gate (anti-hallucination) requires every new third-party import
115
- to appear in a project manifest:
116
- **Exit 3 → STOP:** an imported package is not declared in any manifest (`package.json`, `go.mod`,
117
- `requirements.txt`, `pyproject.toml`, `Pipfile`, `Cargo.toml`): the classic shape of a
118
- hallucinated or typo-squatted dependency. Confirm the name on the registry and declare it via the
119
- package manager, or remove the import; do **not** mark the slice `built`. The gate is deterministic
120
- and fail-open (not a git repo / no manifest / stdlib-only import → rc 0). If the nearest
121
- manifest declares the package, treat the mismatch as a workflow-tool defect under bounded
122
- recovery, not a plan repair or retry-authorization question. Once every gate and doubt
123
- verdict is accepted, a forged slice records successful verification and runs
124
- manifest-only cleanup per [`forge.md`](forge.md). Then close the private window:
113
+ Once every gate and doubt verdict is accepted, a forged slice records successful
114
+ verification and runs manifest-only cleanup per [`forge.md`](forge.md). Then close the
115
+ private window:
125
116
  ```bash
126
117
  devrites-engine reconcile close
127
118
  ```
@@ -66,15 +66,12 @@ and exact changed-file set. Before the root writes any canonical record, run:
66
66
  ```bash
67
67
  devrites-engine reconcile check; echo "reconcile rc=$?"
68
68
  devrites-engine test-integrity; echo "test-integrity rc=$?"
69
- devrites-engine package-existence; echo "package-existence rc=$?"
70
69
  ```
71
70
 
72
71
  - Reconcile `5`: reject the result. Preserve pre-snapshot user work; restore only the
73
72
  unauthorized slice delta; never widen scope from the writer's self-report.
74
73
  - Test integrity `3`: a test was deleted, muted, focused, or de-asserted. Treat it as a
75
74
  Critical protocol failure and correct it through the wright.
76
- - Package existence `3`: verify the exact package/version and fix the bounded workflow
77
- or dependency defect through recovery. It is not a reason to ask for retry approval.
78
75
  - A setup/corrupt-baseline error blocks acceptance; never fall back silently to `HEAD`.
79
76
 
80
77
  The clean reconcile check records that source has not changed since inspection. The
@@ -105,13 +102,13 @@ Before every retry:
105
102
 
106
103
  That refresh requires the prior clean check, re-fingerprints root-owned state, and keeps
107
104
  the original source baseline. Re-dispatch with the exact output, attempt count, and dead
108
- ends. Then repeat reconcile and both integrity gates from zero. Clear recovery only after
105
+ ends. Then repeat reconcile and `test-integrity` from zero. Clear recovery only after
109
106
  green with `recovery clear --class <class> "<root cause>" <slug>`. An exhausted objective
110
107
  failure becomes a technical blocker with its reproduction, not a `rite-resolve` question.
111
108
 
112
109
  ## Close and record
113
110
 
114
- When reconciliation, integrity, package, proof, and doubt gates all pass:
111
+ When reconciliation, integrity, proof, and doubt gates all pass:
115
112
 
116
113
  ```bash
117
114
  devrites-engine reconcile close
@@ -21,7 +21,6 @@ mutation tool when present. Run:
21
21
  ```bash
22
22
  devrites-engine test-integrity
23
23
  devrites-engine mutation-gate
24
- devrites-engine package-existence
25
24
  ```
26
25
 
27
26
  A test that stays green on broken code is unproven. Pure transforms also get a
@@ -53,7 +53,6 @@ gates:
53
53
  ```bash
54
54
  devrites-engine test-integrity; echo "test-integrity rc=$?" # exit 3 = a test deleted/skipped/loosened → Critical NO-GO
55
55
  devrites-engine mutation-gate # changed-files mutation score — band the verdict; survivors are unproven behaviours
56
- devrites-engine package-existence; echo "package-existence rc=$?" # exit 3 = a new import isn't declared in any manifest (hallucinated/typo-squatted dep) → Critical NO-GO
57
56
  ```
58
57
  A test weakened since the slice base is a **Critical NO-GO**: the suite went green by lowering the
59
58
  bar, not by the code being right. Record the mutation score in `seal.md`; under
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devrites",
3
- "version": "3.2.5",
3
+ "version": "3.2.7",
4
4
  "description": "DevRites: a disciplined senior-engineer workflow pack for Claude Code and Codex",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://github.com/ViktorsBaikers/DevRites#readme",
@@ -59,6 +59,7 @@ for item in "${PAYLOAD[@]}"; do
59
59
  if [[ -e "$item" ]]; then
60
60
  if [[ "$item" == "engine" ]] && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
61
61
  while IFS= read -r -d '' path; do
62
+ [[ -e "$path" ]] || continue
62
63
  if [[ "$path" == engine/testdata/golden/* ]]; then
63
64
  continue
64
65
  fi