@useshifu/coding-harness 0.3.3 → 0.3.4

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
@@ -41,7 +41,9 @@ npx @useshifu/coding-harness sync-files --harness codex
41
41
 
42
42
  Automatic scheduled runs send each accepted segment and advance its local checkpoint only after the server returns a matching receipt. Manual scheduled runs rebuild an accumulated local queue without advancing checkpoints and create or resume one content-free harness session when the pending count changes; one approval sends the queue in order. Network failures and server throttling are retried, while rejected segments stay pending.
43
43
 
44
- The same scheduled run checks the input folder. `sync-files` checks it immediately without changing the schedule. Before sending new or changed files, the runner polls unfinished jobs from its local checkpoints. It sends supported file bytes over the configured HTTPS connection (or localhost HTTP for development). The server accepts each file as a background job and processes its text in chunks; upload does not wait for claim extraction. A later scheduled or manual sync polls the job's result. Only `processed`, `no_claims`, or `duplicate` finalizes the local checkpoint; `partial` reopens failed chunks on the next sync. Existing file checkpoints are checked once again after upgrading to this job-based protocol. Files larger than 20 MB remain pending with an error. The scanner reads regular files directly in the chosen folder and ignores symlinks and subfolders. Unknown formats are reported as unsupported rather than treated as successful syncs.
44
+ The same scheduled run checks the input folder. `sync-files` checks it immediately without changing the schedule. Before sending new or changed files, the runner polls unfinished jobs from its local checkpoints. It sends supported file bytes over the configured HTTPS connection (or localhost HTTP for development). The server accepts each file as a background job and processes its text in chunks; upload does not wait for claim extraction. A later scheduled or manual sync polls the job's result. Only `processed`, `no_claims`, or `duplicate` finalizes the local checkpoint; `partial` reopens failed chunks on the next sync. On this upgrade, completed file checkpoints are replayed once so the new extraction can retain suggestions; in-flight jobs are polled first and replayed after finishing. Server-side idempotency prevents already published claims from being duplicated. Files larger than 20 MB remain pending with an error. The scanner reads regular files directly in the chosen folder and ignores symlinks and subfolders. Unknown formats are reported as unsupported rather than treated as successful syncs.
45
+
46
+ The file-job result reports published claims separately from needs-evidence suggestions. A `no_claims` result can still contain suggestions: these are possible contributions awaiting evidence or attribution, not verified claims. The runner reports the suggestion count but never labels those suggestions as established work. It does not print the file's text, candidate statements, or source excerpts to logs. Use Shifu to inspect the suggestions and their evidence status. A result with zero claims and zero suggestions means no user-attributable contribution was extracted; it does not mean the upload failed.
45
47
 
46
48
  The connector captures any useful harness activity, including engineering, product discovery, writing, interview feedback, research, and planning. Version-four payloads support generic `activity` items without a technical scope and accept a safe session-level summary when no item survives redaction. Version 1–3 clients remain supported.
47
49
 
@@ -42,7 +42,7 @@ const MAX_INTERVAL_HOURS = 720;
42
42
  const SCHEDULER_TICK_MINUTES = 15;
43
43
  const MAX_RETRIES = 4;
44
44
  const MAX_WORK_FILE_BYTES = 20 * 1024 * 1024;
45
- const WORK_FILE_SYNC_VERSION = 3;
45
+ const WORK_FILE_SYNC_VERSION = 4;
46
46
  const WORK_FILE_EXTENSIONS = new Set([".txt", ".md", ".rtf", ".docx"]);
47
47
  const FINAL_WORK_FILE_STATUSES = new Set(["processed", "no_claims", "duplicate"]);
48
48
  const ACTIVE_WORK_FILE_STATUSES = new Set(["queued", "processing", "retrying"]);
@@ -53,7 +53,7 @@ Put work logs, meeting transcripts, and other work documents directly in this fo
53
53
 
54
54
  Supported files: .txt, .md, .rtf, and .docx, up to 20 MB each. Subfolders, links, images, and screenshots are not processed. This README is not synced. Unsupported files are counted but never uploaded.
55
55
 
56
- Uploading starts a background job. Shifu checks unfinished jobs before the next scheduled or manual sync; a file is complete only when the server reports its final result. A partial result stays pending for retry.
56
+ Uploading starts a background job. Shifu checks unfinished jobs before the next scheduled or manual sync; a file is complete only when the server reports its final result. A partial result stays pending for retry. A completed file may have published claims, needs-evidence suggestions, or neither; suggestions are not verified claims.
57
57
 
58
58
  Files you add are sent to Shifu for parsing and sanitization; they are not redacted on this device. Only place files here if you want them processed. For named meeting transcripts, set your display name in Shifu Profile so your speaker turns can be matched.
59
59
  `;
@@ -1059,7 +1059,24 @@ async function postWorkFile(config, file, contents) {
1059
1059
  function validWorkFileJob(receipt) {
1060
1060
  return typeof receipt?.id === "string" && receipt.id.length > 0 &&
1061
1061
  (ACTIVE_WORK_FILE_STATUSES.has(receipt.status) || FINAL_WORK_FILE_STATUSES.has(receipt.status) || receipt.status === "partial") &&
1062
- Number.isInteger(receipt.claimsCreated) && receipt.claimsCreated >= 0;
1062
+ Number.isInteger(receipt.claimsCreated) && receipt.claimsCreated >= 0 &&
1063
+ (receipt.candidateCount === undefined || (Number.isInteger(receipt.candidateCount) && receipt.candidateCount >= 0));
1064
+ }
1065
+
1066
+ function workFileResult(job) {
1067
+ const claims = `${job.claimsCreated} claim${job.claimsCreated === 1 ? "" : "s"}`;
1068
+ const candidates = job.candidateCount > 0 ? `, ${job.candidateCount} needs-evidence suggestion${job.candidateCount === 1 ? "" : "s"}` : "";
1069
+ return `${job.status} (${claims}${candidates})`;
1070
+ }
1071
+
1072
+ function workFileEntries(state, folder) {
1073
+ if (state.workFiles?.folder !== folder) return {};
1074
+ const files = state.workFiles.files || {};
1075
+ if (state.workFiles.version === WORK_FILE_SYNC_VERSION) return files;
1076
+ // Replay old completed checkpoints once, but never abandon an accepted job.
1077
+ return Object.fromEntries(Object.entries(files)
1078
+ .filter(([, entry]) => entry && typeof entry === "object" && entry.jobId)
1079
+ .map(([name, entry]) => [name, { ...entry, replayOnCompletion: true }]));
1063
1080
  }
1064
1081
 
1065
1082
  async function getWorkFileJob(config, jobId) {
@@ -1090,7 +1107,7 @@ async function getWorkFileJob(config, jobId) {
1090
1107
 
1091
1108
  function saveWorkFileEntry(harness, folder, name, entry) {
1092
1109
  const state = readState(harness);
1093
- const files = state.workFiles?.folder === folder && state.workFiles.version === WORK_FILE_SYNC_VERSION ? state.workFiles.files || {} : {};
1110
+ const files = workFileEntries(state, folder);
1094
1111
  state.workFiles = { folder, version: WORK_FILE_SYNC_VERSION, files: { ...files, [name]: entry } };
1095
1112
  saveState(harness, state);
1096
1113
  }
@@ -1099,7 +1116,7 @@ async function syncWorkFiles(harness, config = readConfig(harness)) {
1099
1116
  if (typeof config.inputFolder !== "string") throw new Error("No input folder is configured. Run connect again to enable file sync.");
1100
1117
  const folder = validateInputFolder(configuredInputFolder(config));
1101
1118
  const state = readState(harness);
1102
- const prior = state.workFiles?.folder === folder && state.workFiles.version === WORK_FILE_SYNC_VERSION ? state.workFiles.files || {} : {};
1119
+ const prior = workFileEntries(state, folder);
1103
1120
  let submitted = 0;
1104
1121
  const failedFiles = new Set();
1105
1122
  const unverifiedJobs = new Set();
@@ -1108,9 +1125,14 @@ async function syncWorkFiles(harness, config = readConfig(harness)) {
1108
1125
  try {
1109
1126
  const job = await getWorkFileJob(config, entry.jobId);
1110
1127
  if (FINAL_WORK_FILE_STATUSES.has(job.status)) {
1111
- saveWorkFileEntry(harness, folder, name, entry.sha256);
1112
- prior[name] = entry.sha256;
1113
- console.error(`${name}: ${job.status} (${job.claimsCreated} claim${job.claimsCreated === 1 ? "" : "s"}).`);
1128
+ console.error(`${name}: ${workFileResult(job)}.`);
1129
+ if (entry.replayOnCompletion) {
1130
+ prior[name] = { ...entry, status: "partial" };
1131
+ saveWorkFileEntry(harness, folder, name, prior[name]);
1132
+ } else {
1133
+ saveWorkFileEntry(harness, folder, name, entry.sha256);
1134
+ prior[name] = entry.sha256;
1135
+ }
1114
1136
  } else {
1115
1137
  prior[name] = { ...entry, status: job.status };
1116
1138
  saveWorkFileEntry(harness, folder, name, prior[name]);
@@ -1136,7 +1158,7 @@ async function syncWorkFiles(harness, config = readConfig(harness)) {
1136
1158
  saveWorkFileEntry(harness, folder, name, entry);
1137
1159
  prior[name] = entry;
1138
1160
  submitted += 1;
1139
- console.error(`${name}: ${job.status}${FINAL_WORK_FILE_STATUSES.has(job.status) ? ` (${job.claimsCreated} claim${job.claimsCreated === 1 ? "" : "s"})` : " (processing in background)"}.`);
1161
+ console.error(`${name}: ${FINAL_WORK_FILE_STATUSES.has(job.status) ? workFileResult(job) : `${job.status} (processing in background)`}.`);
1140
1162
  } catch (error) {
1141
1163
  failedFiles.add(name);
1142
1164
  console.error(`${name} remains pending: ${error.message}`);
@@ -1383,7 +1405,7 @@ function status(harness) {
1383
1405
  const state = readState(harness);
1384
1406
  const pending = readJSON(pendingPath(harness), { segments: [] });
1385
1407
  const folder = config.inputFolder ? configuredInputFolder(config) : null;
1386
- const entries = state.workFiles?.version === WORK_FILE_SYNC_VERSION && state.workFiles.folder === folder ? Object.values(state.workFiles.files || {}) : [];
1408
+ const entries = folder ? Object.values(workFileEntries(state, folder)) : [];
1387
1409
  let unsupportedFiles = null;
1388
1410
  let inputFolderError = null;
1389
1411
  if (folder) {
@@ -4,7 +4,7 @@ description: Inspect or sync redacted OpenCode activity using the saved Shifu po
4
4
 
5
5
  Run `npx @useshifu/coding-harness status --harness opencode` first.
6
6
 
7
- For an explicit Shifu input-folder sync, run `sync-files --harness opencode`. It polls unfinished jobs before submitting new or changed `.txt`, `.md`, `.rtf`, and `.docx` files up to 20 MB. Upload starts asynchronous server processing; check status on the next manual or scheduled sync. Only a final `processed`, `no_claims`, or `duplicate` result completes the local checkpoint; a `partial` result remains retryable. Unsupported files are counted but not uploaded. No claim-review step is needed. Do not treat another speaker's work or a meeting action item as the user's contribution. The server parses and sanitizes files; the local runner does not redact file contents.
7
+ For an explicit Shifu input-folder sync, run `sync-files --harness opencode`. It polls unfinished jobs before submitting new or changed `.txt`, `.md`, `.rtf`, and `.docx` files up to 20 MB. Upload starts asynchronous server processing; check status on the next manual or scheduled sync. Only a final `processed`, `no_claims`, or `duplicate` result completes the local checkpoint; a `partial` result remains retryable. Completed file checkpoints from the previous runner are replayed once; existing in-flight jobs are polled before replay. Report published claims and needs-evidence suggestions separately. `no_claims` may still include suggestions, which are not verified user contributions. Unsupported files are counted but not uploaded. No claim-review step is needed. Do not treat another speaker's work or a meeting action item as the user's contribution. The server parses and sanitizes files; the local runner does not redact file contents, and file text or excerpts must not appear in logs.
8
8
 
9
9
  If manual approval has queued scheduled work, run `pending --harness opencode`, show the complete redacted list, and ask once. After approval, run `approve --approved --harness opencode`.
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useshifu/coding-harness",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Scheduled, redacted coding-harness activity sync for Shifu",
5
5
  "bin": {
6
6
  "coding-harness": "bin/shifu-harness.js",
@@ -33,9 +33,9 @@ Use only the flags the user wants to change; existing settings are preserved. Th
33
33
 
34
34
  Run `npx @useshifu/coding-harness status --harness codex` to inspect the saved policy, next scheduled run, pending count, and checkpoints.
35
35
 
36
- For an explicit input-folder sync, run `npx @useshifu/coding-harness sync-files --harness codex`. It first polls previously accepted file jobs, then submits new or changed supported files. Uploading starts background processing; it does not wait for extraction. A later manual or scheduled sync polls status again. Only `processed`, `no_claims`, or `duplicate` advances a file's completed checkpoint; `partial` is retried on the next sync. No claim review or manual approval is needed for files. Report submitted, processing, retry-needed, and unsupported counts separately. Do not claim a submitted file was fully processed. If a file fails, leave it pending and report the error.
36
+ For an explicit input-folder sync, run `npx @useshifu/coding-harness sync-files --harness codex`. It first polls previously accepted file jobs, then submits new or changed supported files. Uploading starts background processing; it does not wait for extraction. A later manual or scheduled sync polls status again. Only `processed`, `no_claims`, or `duplicate` advances a file's completed checkpoint; `partial` is retried on the next sync. The upgraded runner replays old completed checkpoints once, while preserving and polling in-flight jobs before their replay. No claim review or manual approval is needed for files. Report submitted, processing, retry-needed, unsupported, published-claim, and needs-evidence suggestion counts separately. Do not claim a submitted file was fully processed. If a file fails, leave it pending and report the error. Never print file text, candidate statements, or excerpts into logs.
37
37
 
38
- The folder runner accepts nonempty `.txt`, `.md`, `.rtf`, and `.docx` files up to 20 MB; the server accepts at most 500,000 extractable characters per file and splits that text into bounded chunks. Empty, malformed, binary, and renamed unsupported files cannot yield claims. A `no_claims` result means processing finished but no sufficiently grounded contribution could be attributed to the user; it does not mean upload failed. For a named transcript, confirm the user's Shifu Profile display name matches their speaker label before expecting automated attribution. Never promise a claim from every file or infer one from an action item. The upload has a transport deadline, but model processing has no upload-request timeout; failed chunks back off and can be retried without republishing completed chunks.
38
+ The folder runner accepts nonempty `.txt`, `.md`, `.rtf`, and `.docx` files up to 20 MB; the server accepts at most 500,000 extractable characters per file and splits that text into bounded chunks. Empty, malformed, binary, and renamed unsupported files cannot yield claims. A `no_claims` result means processing finished with no published claims; it may still have retained needs-evidence suggestions. Suggestions are possible contributions with unresolved evidence or attribution, not verified claims. If both counts are zero, no attributable contribution was extracted; upload itself may still have succeeded. For a named transcript, confirm the user's Shifu Profile display name matches their speaker label before expecting automated attribution. Never promise a claim from every file or infer one from an action item. The upload has a transport deadline, but model processing has no upload-request timeout; failed chunks back off and can be retried without republishing completed chunks.
39
39
 
40
40
  In manual mode, scheduled runs accumulate redacted segments locally. Run:
41
41