@useshifu/coding-harness 0.2.0 → 0.2.1

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
@@ -16,14 +16,14 @@ npx @useshifu/coding-harness sessions --harness codex --hours 24
16
16
  npx @useshifu/coding-harness review --harness codex --session-ref opaque-session-id
17
17
  ```
18
18
 
19
- `review` reads the next contiguous segment of at most 12 unsynced turns locally. Pass the selected `--hours` value to `review` as well; `--all` is reserved for a specifically selected session's older backlog. It shows assistant final notes and all candidate work items; these are suggestions, not proof or guaranteed redaction. Candidate scope is left unset until a reviewer chooses the smallest supported level. If a segment has more than 64 candidates, rerun `sessions` and `review` with `--turns 1` to narrow it before syncing. Review each work statement and area, prepare any verification, and preserve an item's `itemRef` when a later segment revises the same work. User-message records are omitted, but assistant notes may quote sensitive material. Nothing is uploaded by this command. Repeat discovery after each accepted segment to continue the delta.
19
+ `review` reads the next contiguous segment of at most 12 unsynced turns locally. Pass the selected `--hours` value to `review` as well; `--all` is reserved for a specifically selected session's older backlog. It shows assistant final notes, candidate work items, and a draft that leaves required sync and work-item titles blank for the reviewer. Candidates are suggestions, not proof or guaranteed redaction. Candidate scope is left unset until a reviewer chooses the smallest supported level. If a segment has more than 64 candidates, rerun `sessions` and `review` with `--turns 1` to narrow it before syncing. Review each work title, detail, and area, prepare any verification, and preserve an item's `itemRef` when a later segment revises the same work. User-message records are omitted, but assistant notes may quote sensitive material. Nothing is uploaded by this command. Repeat discovery after each accepted segment to continue the delta.
20
20
 
21
21
  To submit a reviewed segment, pipe the exact payload to `sync`. The CLI prints it, asks again, and only then sends it to Shifu. Successful responses advance a local checkpoint; the server rejects out-of-order or changed retries.
22
22
 
23
23
  ```sh
24
- printf '%s' '{"harness":"codex","sessionRef":"opaque-session-id","fromTurn":1,"toTurn":8,"summary":"Implemented a focused change and checked the result.","evidence":[{"kind":"implementation","statement":"Implemented a focused connector change.","scope":"service","area":"connector","itemRef":"work_1","verificationRefs":[0]}],"verification":["Focused tests passed."],"decisions":[],"redactionVersion":2}' | npx @useshifu/coding-harness sync --harness codex
24
+ printf '%s' '{"harness":"codex","sessionRef":"opaque-session-id","fromTurn":1,"toTurn":8,"title":"Hardened connector checkpoints","summary":"Implemented a focused change and checked the result.","evidence":[{"kind":"implementation","title":"Added checkpoint recovery","statement":"Implemented a focused connector change.","scope":"service","area":"connector","itemRef":"work_1","verificationRefs":[0]}],"verification":["Focused tests passed."],"decisions":[],"redactionVersion":3}' | npx @useshifu/coding-harness sync --harness codex
25
25
  ```
26
26
 
27
- The server stores each approved, redacted work-item statement as a separate claim with its scope, optional area and item reference, cited verification, and source receipt. Summary, uncited verification wording, standalone decisions, raw assistant notes, and transcripts are not retained as claim text. `area`, `itemRef`, and `verificationRefs` are optional; citing a verification item applies only to that work item and is displayed separately from its work statement. A decision becomes a claim only when it is included as an `evidence` item with `kind: "decision"`. At least one reviewed work item is required to advance a checkpoint. The complete cross-component contract is in [coding-session-sync-contract.md](../../docs/coding-session-sync-contract.md).
27
+ The server stores each approved sync title and summary, plus every redacted work-item title and statement as a separate claim with its scope, optional area and item reference, cited verification, and source receipt. Uncited verification wording, standalone decisions, raw assistant notes, and transcripts are not retained as claim text. `area`, `itemRef`, and `verificationRefs` are optional; citing a verification item applies only to that work item and is displayed separately from its work statement. A decision becomes a claim only when it is included as an `evidence` item with `kind: "decision"`. At least one reviewed work item is required to advance a checkpoint. The complete cross-component contract is in [coding-session-sync-contract.md](../../docs/coding-session-sync-contract.md).
28
28
 
29
29
  The default API URL is `https://api.useshifu.com`. Override it during local or self-hosted use with `--api-url` on `connect`, or `SHIFU_API_URL`.
@@ -19,14 +19,16 @@ const UNSAFE_CODE_PATTERN = /\b(?:func|class|interface|struct|package|import|sel
19
19
  const CREDENTIAL_ASSIGNMENT_PATTERN = /\b(?:token|secret|password|api[_-]?key)\s*[:=]\s*\S+/i;
20
20
  const CLOUD_CREDENTIAL_PATTERN = /\bAKIA[0-9A-Z]{16}\b|AIza[0-9A-Za-z_-]{20,}/;
21
21
  const EVIDENCE_KINDS = new Set(["implementation", "decision"]);
22
- const EVIDENCE_FIELDS = new Set(["kind", "statement", "scope", "area", "itemRef", "verificationRefs"]);
23
- const SYNC_FIELDS = new Set(["harness", "sessionRef", "fromTurn", "toTurn", "summary", "evidence", "verification", "decisions", "redactionVersion"]);
22
+ const EVIDENCE_FIELDS = new Set(["kind", "title", "statement", "scope", "area", "itemRef", "verificationRefs"]);
23
+ const SYNC_FIELDS = new Set(["harness", "sessionRef", "fromTurn", "toTurn", "title", "summary", "evidence", "verification", "decisions", "redactionVersion"]);
24
24
  const SCOPE_LEVELS = new Set(["unit", "module", "service", "system", "product"]);
25
25
  const WORK_AREAS = new Set(["architecture", "api", "backend", "connector", "database", "documentation", "frontend", "performance", "security", "testing", "tooling"]);
26
26
  const ITEM_REF_PATTERN = /^[A-Za-z0-9_-]{1,80}$/;
27
27
  const MAX_EVIDENCE_ITEMS = 64;
28
28
  const MAX_SYNC_ITEMS = 16;
29
29
  const MAX_TURNS_PER_SEGMENT = 12;
30
+ const MAX_TITLE_LENGTH = 160;
31
+ const MAX_WORK_DETAIL_LENGTH = 600;
30
32
 
31
33
  function configRoot() {
32
34
  return path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "shifu", "coding-harness");
@@ -207,9 +209,9 @@ function reviewCandidates(notes) {
207
209
  for (const [noteIndex, { turn, note }] of notes.entries()) {
208
210
  for (const [lineIndex, line] of note.split("\n").entries()) {
209
211
  const statement = line.trim().replace(/^[-*]\s+/, "");
210
- const kind = /^(decided|chose|selected)\b/i.test(statement) ? "decision"
211
- : /^(implemented|added|built|fixed|changed|created|completed)\b/i.test(statement) ? "implementation" : undefined;
212
- if (!kind || !textIsSafe(statement, 240) || seen.has(`${kind}:${statement}`)) continue;
212
+ if (!textIsSafe(statement, MAX_WORK_DETAIL_LENGTH) || /\b(?:tests?|checks?|build|lint|review)\b.*\b(?:passed|pass|completed|succeeded)\.?$/i.test(statement)) continue;
213
+ const kind = /^(decided|chose|selected|kept|rejected)\b/i.test(statement) ? "decision" : "implementation";
214
+ if (seen.has(`${kind}:${statement}`)) continue;
213
215
  seen.add(`${kind}:${statement}`);
214
216
  const itemRef = `t${turn}_n${noteIndex + 1}_l${lineIndex + 1}`;
215
217
  const areas = [...WORK_AREAS].filter((area) => new RegExp(`\\b${area}\\b`, "i").test(statement));
@@ -395,9 +397,10 @@ function validateSync(input, harness) {
395
397
  if (!input || Object.keys(input).some((field) => !SYNC_FIELDS.has(field))) throw new Error("Remove unsupported fields from the reviewed sync payload.");
396
398
  if (!input || input.harness !== harness || typeof input.sessionRef !== "string" || !textIsSafe(input.sessionRef, 200) || input.sessionRef.length < 8) throw new Error("Add the current opaque sessionRef before syncing.");
397
399
  if (!Number.isInteger(input.fromTurn) || !Number.isInteger(input.toTurn) || input.fromTurn < 1 || input.toTurn < input.fromTurn || input.toTurn - input.fromTurn >= MAX_TURNS_PER_SEGMENT) throw new Error("Use a valid incremental range of at most 12 turns.");
400
+ if (input.redactionVersion === 3 && !textIsSafe(input.title, MAX_TITLE_LENGTH)) throw new Error("Add a concise, redacted title for this reviewed sync.");
398
401
  if (!textIsSafe(input.summary, 1200)) throw new Error("The summary is empty, too long, or contains sensitive content. Redact it before syncing.");
399
- if (!Array.isArray(input.evidence) || input.evidence.length < 1 || input.evidence.length > MAX_EVIDENCE_ITEMS || !input.evidence.every((item) => item && EVIDENCE_KINDS.has(item.kind) && SCOPE_LEVELS.has(item.scope) && textIsSafe(item.statement, 240))) {
400
- throw new Error(`evidence must contain 1-${MAX_EVIDENCE_ITEMS} redacted work items with kind, statement, and scope.`);
402
+ if (!Array.isArray(input.evidence) || input.evidence.length < 1 || input.evidence.length > MAX_EVIDENCE_ITEMS || !input.evidence.every((item) => item && EVIDENCE_KINDS.has(item.kind) && SCOPE_LEVELS.has(item.scope) && (input.redactionVersion !== 3 || textIsSafe(item.title, MAX_TITLE_LENGTH)) && textIsSafe(item.statement, MAX_WORK_DETAIL_LENGTH))) {
403
+ throw new Error(`evidence must contain 1-${MAX_EVIDENCE_ITEMS} redacted work items with title, detail, kind, and scope.`);
401
404
  }
402
405
  for (const field of ["verification", "decisions"]) {
403
406
  if (!Array.isArray(input[field]) || input[field].length > MAX_SYNC_ITEMS || !input[field].every((item) => textIsSafe(item, 240))) throw new Error(`${field} must contain at most ${MAX_SYNC_ITEMS} redacted statements.`);
@@ -411,7 +414,7 @@ function validateSync(input, harness) {
411
414
  item.verificationRefs.every((ref) => Number.isInteger(ref) && ref >= 0 && ref < input.verification.length))))) {
412
415
  throw new Error("Each work item must use a supported area, safe itemRef, and distinct indexes into verification.");
413
416
  }
414
- if (input.redactionVersion !== 2) throw new Error("Use redactionVersion 2.");
417
+ if (input.redactionVersion !== 2 && input.redactionVersion !== 3) throw new Error("Use redactionVersion 3 for new reviewed syncs.");
415
418
  }
416
419
 
417
420
  function validSyncReceipt(receipt, input) {
@@ -507,6 +510,7 @@ function review(harness) {
507
510
  source: "Local assistant final notes for unsynced user turns. Candidates are suggestions, not verified or guaranteed redacted; inspect them before constructing a Shifu payload.",
508
511
  notes,
509
512
  candidates,
513
+ draft: { title: null, summary: null, evidence: candidates.map((candidate) => ({ ...candidate, title: null })) },
510
514
  candidateOverflow: candidates.length > MAX_EVIDENCE_ITEMS,
511
515
  remainingTurns: selected.session.turnCount - toTurn,
512
516
  }, null, 2));
@@ -8,4 +8,4 @@ Use the Shifu reviewed-sync process for the current OpenCode session. Never uplo
8
8
  npx @useshifu/coding-harness sync --harness opencode
9
9
  ```
10
10
 
11
- The payload must contain `harness`, an opaque `sessionRef`, a contiguous `fromTurn`/`toTurn` range of at most 12 turns, a concise redacted `summary`, 1–64 redacted `evidence` work items (`kind`, `statement`, `scope`), up to 16 redacted `verification` items, up to 16 redacted `decisions`, and `redactionVersion: 2`. Evidence describes the work itself; verification records how it was checked. Scope must be one of `unit`, `module`, `service`, `system`, or `product` and must be reviewed rather than guessed from the candidate. Replace names, paths, URLs, credentials, commands, prompts, source code, customer details, and proprietary identifiers with neutral descriptions.
11
+ The payload must contain `harness`, an opaque `sessionRef`, a contiguous `fromTurn`/`toTurn` range of at most 12 turns, a concise redacted sync `title`, a redacted `summary`, 1–64 redacted `evidence` work items (`title`, `kind`, `statement`, `scope`), up to 16 redacted `verification` items, up to 16 redacted `decisions`, and `redactionVersion: 3`. Evidence describes the work itself; verification records how it was checked. Scope must be one of `unit`, `module`, `service`, `system`, or `product` and must be reviewed rather than guessed from the candidate. Replace names, paths, URLs, credentials, commands, prompts, source code, customer details, and proprietary identifiers with neutral descriptions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useshifu/coding-harness",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Reviewed, incremental coding-harness sync for Shifu",
5
5
  "bin": {
6
6
  "coding-harness": "bin/shifu-harness.js",
@@ -26,11 +26,12 @@ Use this structure:
26
26
  "sessionRef": "opaque-session-id",
27
27
  "fromTurn": 1,
28
28
  "toTurn": 12,
29
+ "title": "Hardened connector checkpoints",
29
30
  "summary": "Implemented a narrow change and checked the relevant behaviour.",
30
- "evidence": [{"kind": "implementation", "statement": "Implemented a focused connector change.", "scope": "service"}],
31
+ "evidence": [{"kind": "implementation", "title": "Added checkpoint recovery", "statement": "Implemented a focused connector change.", "scope": "service"}],
31
32
  "verification": ["Focused tests passed."],
32
33
  "decisions": ["Kept the change within the existing connector boundary."],
33
- "redactionVersion": 2
34
+ "redactionVersion": 3
34
35
  }
35
36
  ```
36
37