session-orchestrator 3.17.0 → 3.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 (187) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.cursor/rules/030-wave-execution.mdc +17 -1
  5. package/CHANGELOG.md +105 -412
  6. package/README.md +12 -9
  7. package/SECURITY.md +190 -27
  8. package/agents/AGENTS.md +20 -3
  9. package/agents/code-implementer.md +6 -6
  10. package/agents/db-specialist.md +1 -1
  11. package/agents/qa-strategist.md +31 -6
  12. package/agents/schemas/qa-strategist.schema.json +27 -0
  13. package/agents/schemas/test-writer.schema.json +60 -2
  14. package/agents/security-reviewer.md +1 -1
  15. package/agents/session-reviewer.md +1 -1
  16. package/agents/test-writer.md +29 -10
  17. package/agents/ui-developer.md +1 -1
  18. package/commands/contract-version-bump.md +28 -0
  19. package/commands/portfolio.md +1 -1
  20. package/docs/USER-GUIDE.md +8 -3
  21. package/docs/ci-setup.md +121 -7
  22. package/docs/codex-setup.md +1 -1
  23. package/docs/components.md +6 -6
  24. package/docs/cursor-setup.md +22 -9
  25. package/docs/events-schema.md +5 -1
  26. package/docs/instruction-delivery.md +444 -0
  27. package/docs/rule-authoring.md +58 -9
  28. package/docs/session-config-reference.md +244 -9
  29. package/docs/session-config-template.md +39 -3
  30. package/hooks/_lib/guard-source-loader.mjs +467 -0
  31. package/hooks/_lib/lock-bootstrap.mjs +21 -0
  32. package/hooks/_lib/vcs-create-matcher.mjs +119 -0
  33. package/hooks/config-protection.mjs +0 -0
  34. package/hooks/enforce-commands.mjs +10 -2
  35. package/hooks/hooks-codex.json +1 -1
  36. package/hooks/hooks-cursor.json +11 -2
  37. package/hooks/hooks-pi.json +10 -0
  38. package/hooks/hooks.json +21 -1
  39. package/hooks/on-session-end.mjs +178 -18
  40. package/hooks/on-session-start.mjs +23 -0
  41. package/hooks/post-bash-write-verify.mjs +977 -0
  42. package/hooks/post-subagent-discovery-validator.mjs +256 -41
  43. package/hooks/pre-bash-destructive-guard.mjs +525 -160
  44. package/hooks/pre-bash-issue-budget.mjs +167 -0
  45. package/hooks/pre-bash-sessions-ledger-guard.mjs +627 -0
  46. package/hooks/pre-bash-templates-first.mjs +96 -63
  47. package/hooks/subagent-telemetry.mjs +527 -37
  48. package/package.json +5 -2
  49. package/pi/prompts/contract-version-bump.md +12 -0
  50. package/rules/README.md +32 -0
  51. package/scripts/archive-closed-prds.mjs +12 -22
  52. package/scripts/autopilot-multi.mjs +103 -20
  53. package/scripts/backfill-abandoned-sessions.mjs +160 -4
  54. package/scripts/check-doc-consistency.sh +17 -1
  55. package/scripts/eval-session.mjs +50 -9
  56. package/scripts/fleet-instruction-scan.mjs +141 -0
  57. package/scripts/lib/autopilot/mr-draft.mjs +31 -1
  58. package/scripts/lib/autopilot/worktree-pipeline.mjs +113 -5
  59. package/scripts/lib/backlog-scan.mjs +39 -6
  60. package/scripts/lib/blocked-commands-policy.mjs +340 -0
  61. package/scripts/lib/ci-status-banner.mjs +75 -12
  62. package/scripts/lib/claude-md-budget-lint.mjs +283 -34
  63. package/scripts/lib/command-blocker.mjs +1013 -58
  64. package/scripts/lib/config/config-protection.mjs +2 -1
  65. package/scripts/lib/config/drift-check.mjs +9 -1
  66. package/scripts/lib/config/gitlab-portfolio.mjs +1 -1
  67. package/scripts/lib/config/issue-budget.mjs +123 -0
  68. package/scripts/lib/config/reconcile.mjs +21 -0
  69. package/scripts/lib/config/section-extractor.mjs +121 -1
  70. package/scripts/lib/config-schema.mjs +23 -3
  71. package/scripts/lib/config.mjs +17 -0
  72. package/scripts/lib/convergence-monitor.mjs +49 -3
  73. package/scripts/lib/description-surface.mjs +535 -0
  74. package/scripts/lib/dispatcher/enumerate.mjs +26 -40
  75. package/scripts/lib/ecosystem-wizard/config-writer.mjs +26 -24
  76. package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +1 -1
  77. package/scripts/lib/eval/engine.mjs +47 -5
  78. package/scripts/lib/events.mjs +59 -7
  79. package/scripts/lib/gates/gate-full.mjs +15 -3
  80. package/scripts/lib/gates/gate-helpers.mjs +132 -6
  81. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +28 -8
  82. package/scripts/lib/gitlab-portfolio/aggregator.mjs +8 -2
  83. package/scripts/lib/gitlab-portfolio/cli.mjs +1 -1
  84. package/scripts/lib/handover-gate.mjs +7 -3
  85. package/scripts/lib/harness-audit/categories/category4.mjs +9 -3
  86. package/scripts/lib/instruction-budget-guard.mjs +402 -51
  87. package/scripts/lib/io.mjs +345 -10
  88. package/scripts/lib/issue-budget.mjs +269 -0
  89. package/scripts/lib/issue-close-strip-labels.mjs +39 -9
  90. package/scripts/lib/label-scope.mjs +47 -0
  91. package/scripts/lib/learnings/schema.mjs +43 -3
  92. package/scripts/lib/lock-reaper.mjs +1 -2
  93. package/scripts/lib/memory-proposals/schema.mjs +36 -1
  94. package/scripts/lib/peer-discovery.mjs +645 -0
  95. package/scripts/lib/pi-hook-bridge.mjs +146 -17
  96. package/scripts/lib/product-repo-detect.mjs +9 -8
  97. package/scripts/lib/project-hygiene.mjs +432 -0
  98. package/scripts/lib/quality-gate.mjs +167 -0
  99. package/scripts/lib/recommendations-v0.mjs +1 -1
  100. package/scripts/lib/reconcile/eligibility.mjs +1 -1
  101. package/scripts/lib/reconcile/emitter.mjs +23 -4
  102. package/scripts/lib/reconcile/engine.mjs +147 -39
  103. package/scripts/lib/reconcile/idempotency.mjs +114 -14
  104. package/scripts/lib/reconcile-nudge-banner.mjs +65 -9
  105. package/scripts/lib/resource-probe/evaluate.mjs +70 -4
  106. package/scripts/lib/resource-probe.mjs +19 -0
  107. package/scripts/lib/rule-loader.mjs +6 -0
  108. package/scripts/lib/scope-baseline.mjs +564 -0
  109. package/scripts/lib/scope-gate.mjs +399 -98
  110. package/scripts/lib/session-close-backfill.mjs +61 -6
  111. package/scripts/lib/session-end/phase-skip.mjs +1 -0
  112. package/scripts/lib/session-id.mjs +221 -41
  113. package/scripts/lib/session-lock.mjs +304 -6
  114. package/scripts/lib/session-schema/constants.mjs +22 -3
  115. package/scripts/lib/session-schema/validator.mjs +16 -0
  116. package/scripts/lib/sessions-integrity-banner.mjs +294 -0
  117. package/scripts/lib/sessions-staleness-banner.mjs +121 -12
  118. package/scripts/lib/skill-evolution/idempotency.mjs +135 -16
  119. package/scripts/lib/skill-evolution/mr-opener.mjs +9 -1
  120. package/scripts/lib/spiral-carryover.mjs +142 -30
  121. package/scripts/lib/state-md/mission-status.mjs +53 -3
  122. package/scripts/lib/subagents-schema.mjs +43 -9
  123. package/scripts/lib/test-runner/issue-reconcile.mjs +53 -13
  124. package/scripts/lib/tests-src-ratio.mjs +484 -0
  125. package/scripts/lib/validate/check-agents.mjs +56 -0
  126. package/scripts/lib/validate/check-hooks-symmetry.mjs +244 -10
  127. package/scripts/lib/validate/check-rules.mjs +217 -35
  128. package/scripts/lib/validate/check-test-value-bans.mjs +782 -0
  129. package/scripts/lib/validate/check-unicode-safety.mjs +1 -0
  130. package/scripts/lib/validate-vendored-rules.mjs +10 -2
  131. package/scripts/lib/vault-archive.mjs +17 -2
  132. package/scripts/lib/vault-backfill/glab.mjs +8 -0
  133. package/scripts/lib/vault-mirror/process.mjs +30 -0
  134. package/scripts/lib/vault-mirror/render-sessions.mjs +293 -36
  135. package/scripts/lib/vcs-repo-spec.mjs +362 -0
  136. package/scripts/lib/wave-resource-gate.mjs +115 -11
  137. package/scripts/lib/worktree/listing.mjs +44 -7
  138. package/scripts/mcp-server.sh +17 -3
  139. package/scripts/measure-context-overhead.sh +151 -0
  140. package/scripts/memory-propose.mjs +72 -9
  141. package/scripts/print-applicable-rules.mjs +51 -12
  142. package/scripts/release.mjs +534 -0
  143. package/scripts/run-quality-gate.mjs +123 -5
  144. package/scripts/validate-wave-scope.mjs +182 -17
  145. package/scripts/vault-integration-watcher.mjs +32 -10
  146. package/skills/_shared/config-reading.md +2 -2
  147. package/skills/bootstrap/fast-template.md +1 -1
  148. package/skills/claude-md-drift-check/checker.mjs +145 -28
  149. package/skills/contract-version-bump/SKILL.md +219 -0
  150. package/skills/discovery/SKILL.md +4 -4
  151. package/skills/discovery/issue-templates.md +11 -11
  152. package/skills/discovery/probes-audit.md +1 -1
  153. package/skills/discovery/probes-feature.md +1 -1
  154. package/skills/discovery/probes-session.md +26 -5
  155. package/skills/ecosystem-health/SKILL.md +1 -1
  156. package/skills/ecosystem-health/wizard.md +4 -4
  157. package/skills/evolve/SKILL.md +1 -0
  158. package/skills/gitlab-ops/SKILL.md +20 -12
  159. package/skills/gitlab-portfolio/SKILL.md +2 -2
  160. package/skills/hook-development/SKILL.md +1 -1
  161. package/skills/mode-selector/SKILL.md +1 -1
  162. package/skills/npm-publish/SKILL.md +17 -1
  163. package/skills/plan/SKILL.md +5 -5
  164. package/skills/plan/mode-feature.md +4 -4
  165. package/skills/plan/mode-new.md +10 -10
  166. package/skills/plan/mode-retro.md +1 -1
  167. package/skills/quality-gates/SKILL.md +1 -1
  168. package/skills/reconcile/SKILL.md +21 -4
  169. package/skills/session-end/SKILL.md +34 -13
  170. package/skills/session-end/discovery-scan.md +4 -2
  171. package/skills/session-end/drift-operations.md +4 -4
  172. package/skills/session-end/metrics-collection.md +13 -0
  173. package/skills/session-end/phase-3-2-docs-verification.md +1 -1
  174. package/skills/session-end/phase-3-6-tail.md +2 -1
  175. package/skills/session-end/plan-verification.md +5 -2
  176. package/skills/session-end/vault-operations.md +1 -1
  177. package/skills/session-end/verification-checklist.md +1 -1
  178. package/skills/session-plan/SKILL.md +6 -2
  179. package/skills/session-plan/wave-template.md +2 -0
  180. package/skills/session-start/SKILL.md +73 -7
  181. package/skills/session-start/phase-4-5-resource-health.md +15 -2
  182. package/skills/test-runner/SKILL.md +2 -2
  183. package/skills/vault-sync/validator.mjs +108 -7
  184. package/skills/wave-executor/SKILL.md +5 -2
  185. package/skills/wave-executor/circuit-breaker.md +2 -0
  186. package/skills/wave-executor/wave-loop.md +163 -10
  187. package/templates/_shared/loop.md +4 -4
@@ -22,8 +22,8 @@
22
22
  * G7 transcript inspection via hooks/_lib/transcript-history.mjs.
23
23
  * If any prior Read tool call matches one of the host-specific template
24
24
  * paths from the policy, exit 0.
25
- * G8 fall-through: emit deny via stdout JSON + structured stderr listing the
26
- * template paths, and exit 2.
25
+ * G8 fall-through: emit the PreToolUse deny envelope on stdout via emitDeny
26
+ * (exit 0) with the template-path list + ack hint inside the reason.
27
27
  *
28
28
  * Fail-safe posture: any internal exception is swallowed in main().catch and
29
29
  * the hook exits 0 (allow). Rationale matches pre-bash-destructive-guard.mjs:
@@ -31,14 +31,17 @@
31
31
  * worst case is a missed enforcement, not a wedged session.
32
32
  *
33
33
  * Exit codes:
34
- * 0 — pass-through (G1-G3 short-circuits, bypass match, acknowledgement, Read found, error)
35
- * 2 — deny + structured JSON on stdout (PRD § 3 Gherkin Pattern 3)
34
+ * 0 — every path. Pass-through (G1-G3 short-circuits, bypass match,
35
+ * acknowledgement, Read found, error) emits nothing; G8 emits the deny
36
+ * envelope on stdout. Exit 2 is NEVER used: Claude Code discards stdout
37
+ * on exit 2, which would throw away the deny envelope entirely (#906).
36
38
  */
37
39
 
38
- import { readStdin, emitAllow } from '../scripts/lib/io.mjs';
40
+ import { readStdin, emitAllow, emitDeny } from '../scripts/lib/io.mjs';
39
41
  import { resolveProjectDir, resolvePluginRoot } from '../scripts/lib/platform.mjs';
40
42
  import { readJson } from '../scripts/lib/common.mjs';
41
43
  import { hasReadInSession } from './_lib/transcript-history.mjs';
44
+ import { resolveHost, matchesBypass } from './_lib/vcs-create-matcher.mjs';
42
45
 
43
46
  import { shouldRunHook } from './_lib/profile-gate.mjs';
44
47
  import { existsSync, readdirSync, lstatSync } from 'node:fs';
@@ -54,13 +57,11 @@ if (!shouldRunHook('pre-bash-templates-first')) process.exit(0);
54
57
  // ---------------------------------------------------------------------------
55
58
 
56
59
  /**
57
- * Matches the canonical `gh` / `glab` issue/PR/MR creation invocations.
58
- * Anchored at start (^) with optional leading whitespace to catch indented
59
- * shell snippets. Word-boundary at the end avoids false positives on tokens
60
- * like `created` or `news`. Edit operations (`gh pr edit`, `glab mr edit`)
61
- * are deliberately out of scope per PRD § 2 Out-of-Scope.
60
+ * The `gh`/`glab` create matcher (`CREATE_REGEX`, `resolveHost`,
61
+ * `matchesBypass`) now lives in `hooks/_lib/vcs-create-matcher.mjs` so the
62
+ * sibling issue-budget hook parses create commands with byte-identical
63
+ * semantics instead of a diverging copy. Behaviour here is unchanged.
62
64
  */
63
- const CREATE_REGEX = /^\s*(gh|glab)\s+(pr|mr|issue)\s+(create|new)\b/;
64
65
 
65
66
  /**
66
67
  * Default acknowledgement path, relative to project root. Used when the
@@ -68,14 +69,91 @@ const CREATE_REGEX = /^\s*(gh|glab)\s+(pr|mr|issue)\s+(create|new)\b/;
68
69
  */
69
70
  const DEFAULT_ACK_PATH = '.orchestrator/runtime/templates-acknowledged.json';
70
71
 
72
+ /**
73
+ * Ceiling (in characters) for the raw bash command echoed into the deny reason.
74
+ *
75
+ * ## Why this file needs its own bound at all (#919, follow-up to #906)
76
+ *
77
+ * `command` is the ONE attacker/agent-controlled term in the reason — every
78
+ * other line is fixed text or a repo-derived path list. Echoing it unbounded
79
+ * made this hook the shortest path to the failure mode #906 was repaired for:
80
+ * a reason large enough to push the stdout envelope past the 65 536-byte kernel
81
+ * pipe buffer, where a truncated envelope reads as "no decision" and the tool
82
+ * call is ALLOWED. `emitDeny`'s {@link DENY_REASON_MAX} clamp (16 000) now
83
+ * stands in that path, but a consumer that respects the bound itself is the
84
+ * more robust shape — defence in depth, not reliance on the single downstream
85
+ * clamp.
86
+ *
87
+ * The clamp alone is also NOT sufficient here, which is the concrete bug this
88
+ * constant fixes rather than merely hardens against. `Command:` is line 2 of 6;
89
+ * the template-path list and the `/templates-ack` hint are lines 3-6. A
90
+ * 200 000-char command therefore consumed the entire 16 000-char budget and cut
91
+ * the remedy off the end — the deny still bit, but PRD § 3 Gherkin Pattern 3's
92
+ * required content never reached the reader.
93
+ *
94
+ * ## Why 512 and not the 80 used at the two stderr sites below
95
+ *
96
+ * This file already truncates `command` twice (bypass-matched and
97
+ * no-templates-found), both at `slice(0, 80)`. Those are one-line **stderr log**
98
+ * lines, where 80 is the terminal-width convention — a different consumer class.
99
+ * The repo's precedent for bounding a bash command inside a **structured record
100
+ * field** is `hooks/pre-bash-staging-fence.mjs` (`staged_paths[].command`,
101
+ * `slice(0, 512)`), and `permissionDecisionReason` is exactly that: a structured
102
+ * field, not a log line. Reusing 512 follows the matching house convention
103
+ * instead of inventing a third number. Reusing 80 would clip a realistic
104
+ * `gh pr create --title … --body …` mid-flag, defeating the recognisability the
105
+ * line exists to provide.
106
+ *
107
+ * Bound check: the fixed part of this reason measures 348 chars (see the
108
+ * call-site table in `scripts/lib/io.mjs`), so the worst case is 348 + 512 +
109
+ * the path list — ~19× below `DENY_REASON_MAX` and ~76× below the pipe buffer.
110
+ * This hook no longer contributes an unbounded term to the envelope at all.
111
+ */
112
+ const COMMAND_ECHO_MAX = 512;
113
+
71
114
  // ---------------------------------------------------------------------------
72
115
  // Helpers
73
116
  // ---------------------------------------------------------------------------
74
117
 
75
118
  /**
76
- * Block the create command: write structured deny JSON to stdout + exit 2.
77
- * Mirrors the exact format from pre-bash-destructive-guard.mjs blockCommand()
78
- * so downstream Claude Code rendering is consistent.
119
+ * Clip the echoed command to {@link COMMAND_ECHO_MAX}, marking the cut so the
120
+ * reader can tell the command was truncated rather than ending there.
121
+ *
122
+ * The marker is budgeted INSIDE the ceiling — mirroring `_clampReason` in
123
+ * `scripts/lib/io.mjs` — so the returned string never exceeds it and the
124
+ * worst-case reason length stays a fixed, auditable number.
125
+ *
126
+ * @param {string} command
127
+ * @returns {string}
128
+ */
129
+ function clipCommand(command) {
130
+ if (command.length <= COMMAND_ECHO_MAX) return command;
131
+ const marker = `… [truncated: showing ${COMMAND_ECHO_MAX} of ${command.length} characters]`;
132
+ return command.slice(0, COMMAND_ECHO_MAX - marker.length) + marker;
133
+ }
134
+
135
+ /**
136
+ * Block the create command: emit the PreToolUse deny envelope via emitDeny
137
+ * (exit 0). Mirrors pre-bash-destructive-guard.mjs blockCommand() so downstream
138
+ * Claude Code rendering is consistent.
139
+ *
140
+ * PRD § 3 Gherkin Pattern 3 requires the template-path list plus the
141
+ * `/templates-ack` hint to reach the reader. That requirement is UNCHANGED —
142
+ * only the channel moved (#906). Until #906 this text was written to stderr AND
143
+ * duplicated into an `exit 2` stdout envelope, the mixed form the hook docs
144
+ * forbid ("choose one approach per hook, not both"). Under exit 0, stderr is
145
+ * only surfaced in the debug log — invisible to both operator and model — so a
146
+ * stderr write would look alive while being dead. The full multi-line text
147
+ * therefore travels as the emitDeny `reason`, landing in
148
+ * `permissionDecisionReason`, which is fed to **Claude** — the actor that has
149
+ * to read the template or run `/templates-ack`. The operator sees the first
150
+ * line via the derived `systemMessage` headline.
151
+ *
152
+ * The echoed `command` is bounded by {@link COMMAND_ECHO_MAX} before it enters
153
+ * the reason: it is the only agent-controlled term here, and left unbounded it
154
+ * both risked the pipe-buffer fail-open #906 repaired AND pushed the template
155
+ * list plus the ack hint past `emitDeny`'s clamp — i.e. truncated away exactly
156
+ * the content the PRD requires. See that constant for the full rationale.
79
157
  *
80
158
  * @param {{ host: string, command: string, templatePaths: string[],
81
159
  * ackFile: string }} ctx
@@ -88,21 +166,16 @@ function blockCreate(ctx) {
88
166
  : ' (none configured)';
89
167
  const reason = [
90
168
  `pre-bash-templates-first: ${host} create call detected without prior template Read.`,
91
- `Command: ${command}`,
169
+ `Command: ${clipCommand(command)}`,
92
170
  `Found templates:`,
93
171
  pathList,
94
172
  `Read one of these first, OR run \`/templates-ack\` (writes ${ackFile}) to bypass for this session.`,
95
173
  `See: issue #519, "gsd Pattern Adoption Quick-Wins" (archived in the private Meta-Vault) (Pattern 3)`,
96
174
  ].join('\n');
97
175
 
98
- // PRD § 3 Gherkin Pattern 3 spec: stderr lists template paths + ack hint.
99
- // We emit BOTH stderr (human-readable per spec) AND the structured stdout
100
- // JSON envelope (machine-readable for Claude Code hook protocol).
101
- process.stderr.write(reason + '\n');
102
- process.stdout.write(
103
- JSON.stringify({ permissionDecision: 'deny', reason }) + '\n',
104
- );
105
- process.exit(2);
176
+ // Single channel: the full multi-line reason (template paths + ack hint)
177
+ // rides in permissionDecisionReason. Never returns.
178
+ emitDeny(reason);
106
179
  }
107
180
 
108
181
  /**
@@ -142,46 +215,6 @@ function resolvePolicyPath(projectDir) {
142
215
  return null;
143
216
  }
144
217
 
145
- /**
146
- * Determine which host the command targets by inspecting the CREATE_REGEX
147
- * capture. `gh` → "github", `glab` → "gitlab".
148
- *
149
- * @param {string} command
150
- * @returns {"github"|"gitlab"|null}
151
- */
152
- function resolveHost(command) {
153
- const m = command.match(CREATE_REGEX);
154
- if (!m) return null;
155
- return m[1] === 'gh' ? 'github' : 'gitlab';
156
- }
157
-
158
- /**
159
- * True when the command starts with any of the bypass patterns. Bypass match
160
- * is a prefix check with a word/EOL boundary on the trailing edge — this
161
- * prevents trivial bypass via prefix-inclusion (e.g. policy entry
162
- * "gh issue create --label bot" must not match "gh issue create --label botanical").
163
- *
164
- * @param {string} command
165
- * @param {string[]} bypassPatterns
166
- * @returns {boolean}
167
- */
168
- function matchesBypass(command, bypassPatterns) {
169
- if (!Array.isArray(bypassPatterns) || bypassPatterns.length === 0) {
170
- return false;
171
- }
172
- const stripped = command.replace(/^\s+/, '');
173
- for (const pat of bypassPatterns) {
174
- if (typeof pat !== 'string' || pat.length === 0) continue;
175
- const patStripped = pat.replace(/^\s+/, '');
176
- if (!stripped.startsWith(patStripped)) continue;
177
- // Boundary check: next character must be whitespace, EOL, or absent.
178
- // This prevents "gh foo --label bot" from matching policy "gh foo --label botanical".
179
- const nextChar = stripped.charAt(patStripped.length);
180
- if (nextChar === '' || /\s/.test(nextChar)) return true;
181
- }
182
- return false;
183
- }
184
-
185
218
  /**
186
219
  * Check the acknowledgement file for the current session_id. Best-effort:
187
220
  * any read or parse error means "no acknowledgement" (returns false) so the