patchwork-os 0.2.0-beta.13.canary.300 → 0.2.0-beta.13.canary.302

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/dist/index.js CHANGED
@@ -161,6 +161,7 @@ const KNOWN_SUBCOMMANDS = [
161
161
  "shadow-scan",
162
162
  "workers",
163
163
  "approvals",
164
+ "gate",
164
165
  ];
165
166
  const __invokedSubcommand = (() => {
166
167
  const sub = process.argv[2];
@@ -3937,6 +3938,76 @@ if (process.argv[2] === "approvals") {
3937
3938
  }
3938
3939
  })();
3939
3940
  }
3941
+ // Handle gate subcommand — "why did the worker allow/gate THIS action" over
3942
+ // the local Decision Record (worker_gate_decisions.jsonl). Reads the log
3943
+ // directly (like `workers shadow` / `approvals`); no bridge round-trip needed
3944
+ // since the record is a local file, not live state.
3945
+ if (process.argv[2] === "gate") {
3946
+ const args = process.argv.slice(3);
3947
+ const sub = args[0];
3948
+ if (sub !== "explain" ||
3949
+ args.includes("--help") ||
3950
+ args.includes("-h") ||
3951
+ args.length < 3) {
3952
+ process.stdout.write("Usage: patchwork gate explain <workerId> <classKey> [--limit N] [--diff] [--json]\n\n" +
3953
+ "Explain the worker-autonomy gate's most recent decision(s) for a given\n" +
3954
+ "worker × action-class, from the local Decision Record\n" +
3955
+ "(~/.patchwork/worker_gate_decisions.jsonl) — no bridge required.\n\n" +
3956
+ ' <classKey> e.g. "issue:compensable:high" (domain:reversibility:blastTier)\n' +
3957
+ " --limit N show the N most recent decisions (default 1, or 2 with --diff)\n" +
3958
+ " --diff compare the 2 most recent decisions and show what changed\n" +
3959
+ " --json emit raw JSON (for scripting)\n");
3960
+ process.exit(sub === "explain" ? 1 : 0);
3961
+ }
3962
+ (async () => {
3963
+ try {
3964
+ // Length checked above (args.length < 3 short-circuits to usage), so
3965
+ // args[1]/[2] are present here — noUncheckedIndexedAccess just can't see
3966
+ // across the `if` block above.
3967
+ const workerId = args[1];
3968
+ const classKey = args[2];
3969
+ const wantDiff = args.includes("--diff");
3970
+ const limitIdx = args.indexOf("--limit");
3971
+ const explicitLimit = limitIdx >= 0 && limitIdx + 1 < args.length
3972
+ ? Number.parseInt(args[limitIdx + 1], 10)
3973
+ : undefined;
3974
+ const limit = explicitLimit ?? (wantDiff ? 2 : 1);
3975
+ if (!Number.isFinite(limit) || limit < 1) {
3976
+ process.stderr.write("Error: --limit must be a positive integer\n");
3977
+ process.exit(1);
3978
+ }
3979
+ const { WorkerGateDecisionLog, formatGateDecisionHistory, formatGateDecisionDiff, } = await import("./workerGateDecisionLog.js");
3980
+ const patchworkDir = process.env.PATCHWORK_HOME ?? path.join(os.homedir(), ".patchwork");
3981
+ const log = new WorkerGateDecisionLog({ dir: patchworkDir });
3982
+ const decisions = log.query({ workerId, classKey, limit });
3983
+ if (args.includes("--json")) {
3984
+ process.stdout.write(`${JSON.stringify(decisions, null, 2)}\n`);
3985
+ process.exit(0);
3986
+ }
3987
+ if (decisions.length === 0) {
3988
+ process.stdout.write(`No gate decisions found for worker "${workerId}" on class "${classKey}".\n` +
3989
+ "Either the worker hasn't acted on this class yet, or worker.autonomy is off.\n");
3990
+ process.exit(0);
3991
+ }
3992
+ if (wantDiff) {
3993
+ // query() sorts most-recent-first: newer is [0], older is [1].
3994
+ const [newer, older] = decisions;
3995
+ if (!newer || !older) {
3996
+ process.stdout.write(`Only one decision found for worker "${workerId}" on class "${classKey}" — nothing to diff against yet.\n`);
3997
+ process.exit(0);
3998
+ }
3999
+ process.stdout.write(`${formatGateDecisionDiff(newer, older)}\n`);
4000
+ process.exit(0);
4001
+ }
4002
+ process.stdout.write(`${formatGateDecisionHistory(decisions)}\n`);
4003
+ process.exit(0);
4004
+ }
4005
+ catch (err) {
4006
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
4007
+ process.exit(1);
4008
+ }
4009
+ })();
4010
+ }
3940
4011
  // Handle launchd subcommand — install/uninstall macOS LaunchAgent for auto-start
3941
4012
  if (process.argv[2] === "launchd") {
3942
4013
  const sub = process.argv[3];