@crafts69guy/orchestra 0.4.0 → 0.5.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orch",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Orchestrator workflow: scope, implement, review, ship via specialised subagents and worktrees.",
5
5
  "author": {
6
6
  "name": "crafts69guy"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orch",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Orchestrator workflow: scope, implement, review, ship via specialised subagents and worktrees.",
5
5
  "author": {
6
6
  "name": "crafts69guy",
package/README.md CHANGED
@@ -59,6 +59,9 @@ permission model. Other arguments pass through to the harness, and so does its e
59
59
  `orch claude` only sets `ORCH=1`; `ORCH=1 claude` works too. Without `ORCH=1` the hooks exit
60
60
  silently and the session is untouched.
61
61
 
62
+ The handoff brief to `implementer` includes a Docs/JSDoc acceptance criterion (new or changed exports
63
+ carry JSDoc, touched docs are updated). `ORCH_DOCS=0` turns it off; any other value keeps it on.
64
+
62
65
  ## Codex
63
66
 
64
67
  Codex does not trust project agents or plugin hooks by default, so `orch codex` runs `codex`
@@ -20,3 +20,4 @@ Report with these sections:
20
20
  - Changes: files touched and what changed.
21
21
  - Validation: each command you ran and its result, as evidence.
22
22
  - Notes: deviations, open questions, anything unverified.
23
+ - Docs: if the brief includes Docs/JSDoc, list the JSDoc and docs you added.
@@ -18,4 +18,5 @@ Report with these sections:
18
18
  - Changes: files touched and what changed.
19
19
  - Validation: each command you ran and its result, as evidence.
20
20
  - Notes: deviations, open questions, anything unverified.
21
+ - Docs: if the brief includes Docs/JSDoc, list the JSDoc and docs you added.
21
22
  '''
@@ -30,9 +30,18 @@ export function which(name, pathEnv = '') {
30
30
  });
31
31
  }
32
32
 
33
+ /** Lines ending in `<!-- orch:docs -->` are the Docs/JSDoc criterion: dropped when off, marker stripped when on. */
34
+ export function applyDocs(rules, docs) {
35
+ return rules
36
+ .split('\n')
37
+ .filter((l) => docs || !l.includes('<!-- orch:docs -->'))
38
+ .map((l) => l.replace(/ *<!-- orch:docs -->$/, ''))
39
+ .join('\n');
40
+ }
41
+
33
42
  /** `orchPath` is the absolute `bin/orch`; plugin bin/ is not on PATH under Codex, so name it. */
34
- export function buildContext(rules, hasWt, orchPath) {
35
- const withCli = `${rules.trimEnd()}\n\norch CLI: ${orchPath} (use \`${orchPath} wt new|merge|list\`)\n`;
43
+ export function buildContext(rules, hasWt, orchPath, docs = true) {
44
+ const withCli = `${applyDocs(rules, docs).trimEnd()}\n\norch CLI: ${orchPath} (use \`${orchPath} wt new|merge|list\`)\n`;
36
45
  return hasWt ? withCli : `${withCli}\n${WT_WARNING}\n`;
37
46
  }
38
47
 
@@ -50,7 +59,16 @@ export function run({ env, root, read, hasCommand, stdout, stderr }) {
50
59
  stderr(`orch: rules/orchestrator.md not found under ${root}; skipping injection\n`);
51
60
  return 0;
52
61
  }
53
- stdout(formatOutput(buildContext(rules, hasCommand('wt', env.PATH), join(root, 'bin', 'orch'))));
62
+ stdout(
63
+ formatOutput(
64
+ buildContext(
65
+ rules,
66
+ hasCommand('wt', env.PATH),
67
+ join(root, 'bin', 'orch'),
68
+ env.ORCH_DOCS !== '0',
69
+ ),
70
+ ),
71
+ );
54
72
  return 0;
55
73
  }
56
74
 
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
2
3
  import { readStdin, roleOf } from './guard.mjs';
3
4
  import { isEntry } from './session-start.mjs';
4
5
 
@@ -13,8 +14,36 @@ export function hasEvidence(message) {
13
14
  return typeof message === 'string' && STATUS.test(message) && VALIDATION.test(message);
14
15
  }
15
16
 
17
+ /**
18
+ * The `message` of the last `SubagentHandback` tool call in a subagent transcript (JSONL), or
19
+ * undefined when the path is missing, unreadable or holds no handback. Never throws.
20
+ */
21
+ export function handbackReport(path, readFile = (p) => readFileSync(p, 'utf8')) {
22
+ if (typeof path !== 'string' || path === '') return undefined;
23
+ let report;
24
+ try {
25
+ for (const line of readFile(path).split('\n')) {
26
+ let content;
27
+ try {
28
+ content = JSON.parse(line)?.message?.content;
29
+ } catch {
30
+ continue;
31
+ }
32
+ if (!Array.isArray(content)) continue;
33
+ for (const part of content) {
34
+ if (part?.type === 'tool_use' && part.name === 'SubagentHandback') {
35
+ report = part.input?.message;
36
+ }
37
+ }
38
+ }
39
+ } catch {
40
+ return undefined;
41
+ }
42
+ return report;
43
+ }
44
+
16
45
  /** Returns the exit code; never throws, bad stdin JSON is a silent allow. */
17
- export function run({ env, input, stdout }) {
46
+ export function run({ env, input, stdout, readFile }) {
18
47
  if (env.ORCH !== '1') return 0;
19
48
  let payload;
20
49
  try {
@@ -23,7 +52,10 @@ export function run({ env, input, stdout }) {
23
52
  return 0;
24
53
  }
25
54
  if (roleOf(payload?.agent_type) !== 'implementer' || payload?.stop_hook_active === true) return 0;
26
- if (!hasEvidence(payload?.last_assistant_message)) {
55
+ if (
56
+ !hasEvidence(payload?.last_assistant_message) &&
57
+ !hasEvidence(handbackReport(payload?.agent_transcript_path, readFile))
58
+ ) {
27
59
  stdout(JSON.stringify({ decision: 'block', reason: BLOCK_REASON }));
28
60
  }
29
61
  return 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crafts69guy/orchestra",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Cross-harness (Claude Code + Codex) orchestration workflow plugin",
6
6
  "license": "MIT",
@@ -13,6 +13,7 @@ Subagents: `scoper`, `implementer`, `reviewer`, `shipper`. On Claude Code call t
13
13
  - **Parallel:** use the `dispatch` skill without asking when a plan has 2+ independent tasks (no `depends_on` path, disjoint `files`) that are each substantial: its own module or feature needing several edit-and-test cycles, roughly 100+ changed lines. Independent small tasks go to one `implementer` as a single batched task, done in order, not to dispatch. Never run two writers on the same checkout.
14
14
  Measured: dispatch was no faster on 4 tiny modules (115 s vs 118 s, +45% cost) and 21% faster on 3 large ones (+22% cost).
15
15
  - **Review:** review every implementer diff yourself and rerun its validation. Use `reviewer` (fresh context: diff, brief, acceptance criteria) for risky changes (security, concurrency, migrations, public APIs) or on request. At most 2 fix rounds.
16
+ Also check the Docs/JSDoc criterion. <!-- orch:docs -->
16
17
  - **Shipping:** delegate commit, push and PR to `shipper` only after review passes and the user asked to ship.
17
18
  - **Stays with you:** scope, architecture, cross-cutting decisions, root-cause analysis, merge conflicts, escalations.
18
19
 
@@ -20,6 +21,7 @@ Subagents: `scoper`, `implementer`, `reviewer`, `shipper`. On Claude Code call t
20
21
 
21
22
  Subagents do not see this conversation. Decide interfaces, names and libraries before delegating, then send:
22
23
  objective · decisions already made · files in scope and out of scope · constraints · validation command · stop conditions.
24
+ Docs/JSDoc: new or changed exported types, functions and their fields carry JSDoc in the repo's doc style, and docs the change touches (ADRs, glossary, guides) are updated; the orchestrator checks this when reviewing the diff. <!-- orch:docs -->
23
25
  Point to the plan/brief file and section instead of restating it. Pass paths and commit SHAs, not "what we discussed".
24
26
 
25
27
  ## Results
@@ -63,7 +63,12 @@ export function run({ argv, env, root, cwd, read, readdir, hasCommand, gitDir, s
63
63
  stderr(`orch codex: rules/orchestrator.md not found under ${root}\n`);
64
64
  return 1;
65
65
  }
66
- const context = buildContext(rules, hasCommand('wt', env.PATH), join(root, 'bin', 'orch'));
66
+ const context = buildContext(
67
+ rules,
68
+ hasCommand('wt', env.PATH),
69
+ join(root, 'bin', 'orch'),
70
+ env.ORCH_DOCS !== '0',
71
+ );
67
72
  const prefix = buildArgs({
68
73
  context,
69
74
  agents: agentArgs(root, { read, readdir }),