unitbob 0.4.1 → 0.4.3

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.
package/README.md CHANGED
@@ -30,7 +30,7 @@ claude plugin install unitbob@unitbob
30
30
  ```
31
31
  codex plugin marketplace add sergeygershun/unitbob-connector
32
32
  codex plugin add unitbob@unitbob
33
- npx -y unitbob@0.4.1 codex-install
33
+ npx -y unitbob@0.4.3 codex-install
34
34
  ```
35
35
 
36
36
  Start a new Claude Code or Codex thread so the installed skill and named agents
@@ -33,24 +33,11 @@ export async function runLocal(config, args = [], deps) {
33
33
  // examined the suite.
34
34
  if (!ran)
35
35
  continue;
36
- const spent = spend(config.projectRoot, `run-local:${suiteKind}`);
37
- if (request.budget && spent > request.budget.repair_rounds) {
38
- d.stdout.write(polishedEnoughNotice(suiteKind, spent, request.budget.repair_rounds));
39
- }
36
+ // Kept as compatibility diagnostics only. The bounded repair role owns the
37
+ // mechanical ceiling; this counter never stops execution or classifies reds.
38
+ spend(config.projectRoot, `run-local:${suiteKind}`);
40
39
  }
41
40
  }
42
- // Not a refusal, and deliberately not about the budget either. After eight
43
- // rounds of repair the interesting fact is not that a number ran out — it is
44
- // what the remaining reds most likely are. Both real logs show 3-5 runs of a
45
- // branch as ordinary work, so a branch on its ninth has already been repaired
46
- // past the point where the harness is the usual explanation.
47
- function polishedEnoughNotice(suiteKind, spent, allowed) {
48
- return (`\nThat was run ${spent} of the ${suiteKind} branch; this build budgeted ${allowed}. ` +
49
- 'Reds that survive this many rounds of repair are far more likely to be defects of your product ' +
50
- 'than of the harness around it.\n' +
51
- 'Publish the suite as it stands rather than keep polishing. A first suite that comes out red is ' +
52
- 'a finding, not a failure — finding those reds is what it was written to do.\n');
53
- }
54
41
  // Which branches to run. No argument runs every branch the request asked for —
55
42
  // the same "one suite, one run" shape both recipes insist on, so the default
56
43
  // never teaches the habit the recipes forbid. A named branch is for the repair
@@ -74,14 +74,25 @@ function validateCompactFacts(value, label, errors) {
74
74
  return;
75
75
  }
76
76
  for (const [index, entry] of value.entries()) {
77
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
78
+ errors.push(`${label}: $.facts[${index}] must be an object with fact and source_refs; got ${jsonType(entry)}`);
79
+ continue;
80
+ }
77
81
  const fact = entry;
78
- if (!fact || typeof fact.fact !== 'string' || !fact.fact.trim())
82
+ if (typeof fact.fact !== 'string' || !fact.fact.trim())
79
83
  errors.push(`${label}: facts[${index}].fact must be non-empty`);
80
- if (!Array.isArray(fact?.source_refs) || fact.source_refs.some((ref) => typeof ref !== 'string' || !ref.trim())) {
84
+ if (!Array.isArray(fact.source_refs) || fact.source_refs.some((ref) => typeof ref !== 'string' || !ref.trim())) {
81
85
  errors.push(`${label}: facts[${index}].source_refs must be compact source references`);
82
86
  }
83
- if ('source' in (fact ?? {}) || 'transcript' in (fact ?? {}) || 'suite' in (fact ?? {})) {
87
+ if ('source' in fact || 'transcript' in fact || 'suite' in fact) {
84
88
  errors.push(`${label}: facts[${index}] may not embed source, transcript, or suite copies`);
85
89
  }
86
90
  }
87
91
  }
92
+ function jsonType(value) {
93
+ if (value === null)
94
+ return 'null';
95
+ if (Array.isArray(value))
96
+ return 'array';
97
+ return typeof value;
98
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unitbob",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Unitbob connector — thin local hands for the Unitbob Rails brain. Owns no domain logic: it runs tools, relays bytes over the wire, and prints what the server returns.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,26 +1,51 @@
1
1
  name = "suite-repair-worker"
2
- description = "Completes one bounded Unitbob failure packet using the prior checkpoint and owned files, without widening scope or running the suite."
2
+ description = "Completes and executes one bounded Unitbob owned-slice repair packet without widening scope."
3
3
  model = "gpt-5.6-terra"
4
4
  model_reasoning_effort = "medium"
5
5
  developer_instructions = '''
6
- You receive one failure packet: one validated plan item, its checkpoint, owned
7
- paths, and only related failures or stack traces. This is your complete scope.
6
+ You receive one failure packet: one validated plan item, its checkpoint, branch,
7
+ owned paths and case markers, and only related failures or stack traces. This is
8
+ your complete write scope.
8
9
 
9
10
  Complete `unresolved_promises` first while preserving every completed file and
10
- decision. Then repair only harness problems whose stack points into this slice's
11
- owned files. Do not expand capabilities, promises, planned cases, or paths. Do
12
- not edit host-owned shared files, the connector-owned harness, application
13
- production code, or another slice.
11
+ decision. You may read the plan item's source paths and, only as needed for the
12
+ owned diagnosis, stack-referenced project source, the runner setup and harness
13
+ actually in use, helpers, and factories. Repair only this slice's owned generated
14
+ files and its checkpoint. Do not expand capabilities, promises, planned cases,
15
+ markers, or paths. Do not edit production code, host-owned shared files, the
16
+ connector-owned harness, or another slice.
17
+
18
+ After every owned edit, run
19
+ `npx -y --loglevel=error unitbob@0.4.3 run-local <branch>` and inspect the machine
20
+ report. Look only at examples or scenarios matching your owned paths or case
21
+ markers. Do not require a green exit code from the whole branch: foreign failures
22
+ and an already-confirmed product red do not widen your scope. Repeat the bounded
23
+ `edit → run-local → inspect` loop until every owned case passes or is diagnosed
24
+ as a product defect. Do not run the project's suite directly, boot a dev server,
25
+ or invoke an arbitrary runner command.
26
+
27
+ A product-defect diagnosis must briefly name the violated business contract, the
28
+ reason, and production source references. Never delete a planned case, marker,
29
+ capability binding, or assertion; never add `skip`, `pending`, `todo`, or weaken a
30
+ business promise for green. You may correct a generated expectation only when
31
+ the business promise remains intact and source confirms the correction. If the
32
+ harness is still wrong, continue the loop. If the outcome is ambiguous, the
33
+ runner is unusable, or the turn ceiling stops unfinished work, leave an honest
34
+ branch `build_error`, never a product red. No strict JSON handoff is required.
14
35
 
15
36
  Update the same checkpoint as promises complete. Keep facts compact and
16
- source-referenced. Never run the suite or boot the application; the coordinator
17
- owns the single final run. Do not delegate a second repair, continue another
18
- agent, or request another repair round. If work remains at the turn ceiling,
19
- record it in `unresolved_promises` so the coordinator can produce an honest
20
- branch `build_error`.
37
+ source-referenced. The normative JSON shape of one facts entry is:
38
+ ```json
39
+ {"fact":"The route creates an order.","source_refs":["app/orders.rb:12"]}
40
+ ```
41
+ Every facts entry is an object in that shape, never a string. Before handoff,
42
+ make one final read of the checkpoint and confirm every `facts` entry is an
43
+ object in the normative shape above. Do not delegate repair or auto-resume after
44
+ the ceiling. Preserve files and checkpoint for the coordinator's existing
45
+ `Continue once / Stop` choice; record unfinished work in `unresolved_promises`.
21
46
  '''
22
47
 
23
48
  [features.rollout_budget]
24
49
  enabled = true
25
- limit_tokens = 15000
50
+ limit_tokens = 40000
26
51
  reminder_at_remaining_tokens = [3000]
@@ -16,7 +16,12 @@ native budget stop, preserve the supplied checkpoint and completed files and
16
16
  continue only its `unresolved_promises`; never initialize that checkpoint again.
17
17
  Update the checkpoint after every completed promise. Also keep `known_problems` as a compact
18
18
  array of precise unresolved harness problems (empty when none are known). Facts are short statements with
19
- source references; never embed source files, suite copies, or transcript.
19
+ source references. The normative JSON shape of one facts entry is:
20
+ ```json
21
+ {"fact":"The route creates an order.","source_refs":["app/orders.rb:12"]}
22
+ ```
23
+ Every facts entry is an object in that shape, never a string. Never embed source
24
+ files, suite copies, or transcript.
20
25
 
21
26
  Read only the initial `source_paths` and dependencies needed for the finite
22
27
  planned cases. Ask closed questions with the files to look in. For a closed
@@ -31,7 +36,9 @@ copy it into an owned file.
31
36
 
32
37
  Never run the suite, boot the application, or perform branch-global duplicate,
33
38
  marker, metadata, or surface validation. You may make one final read of your
34
- owned files before handoff. Do not create temporary self-validation scripts or
39
+ owned files before handoff. During that final read, confirm every `facts` entry
40
+ is an object in the normative shape above and correct the checkpoint if it is
41
+ not. Do not create temporary self-validation scripts or
35
42
  loop over repeated rereads. If the turn ceiling arrives, leave partial files and
36
43
  an accurate checkpoint; the coordinator will rotate unresolved work into one
37
44
  fresh repair task.