@saasontools/strauss-kb 0.1.5 → 0.1.6

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/cli-main.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runKbCli
4
- } from "./chunk-KQMGKSPZ.js";
5
- import "./chunk-FZIMFPGR.js";
4
+ } from "./chunk-V7TRZ2ER.js";
5
+ import "./chunk-PNSRTKYN.js";
6
6
 
7
7
  // src/cli-main.ts
8
8
  runKbCli(process.argv.slice(2)).catch((error) => {
package/dist/index.cjs CHANGED
@@ -52,6 +52,7 @@ __export(index_exports, {
52
52
  KbPinsMalformedError: () => KbPinsMalformedError,
53
53
  KbRecordAlreadyExistsError: () => KbRecordAlreadyExistsError,
54
54
  KbRecordNotFoundError: () => KbRecordNotFoundError,
55
+ KbSelfVerificationError: () => KbSelfVerificationError,
55
56
  KbStore: () => KbStore,
56
57
  KbWriteConflictError: () => KbWriteConflictError,
57
58
  LOG_FILE: () => LOG_FILE,
@@ -82,6 +83,7 @@ __export(index_exports, {
82
83
  kbLogEntrySchema: () => kbLogEntrySchema,
83
84
  kbRecordFrontmatterSchema: () => kbRecordFrontmatterSchema,
84
85
  kbSourceSchema: () => kbSourceSchema,
86
+ kbVerifiedEventSchema: () => kbVerifiedEventSchema,
85
87
  listPins: () => listPins,
86
88
  loadQmd: () => loadQmd,
87
89
  matchToDiff: () => matchToDiff,
@@ -154,6 +156,11 @@ var kbActorStampSchema = import_zod.z.object({
154
156
  by: import_zod.z.string().min(1),
155
157
  at: import_zod.z.string().min(1)
156
158
  }).passthrough();
159
+ var kbVerifiedEventSchema = kbActorStampSchema.extend({
160
+ note: import_zod.z.string().refine((s) => s.trim().length > 0, {
161
+ message: "note must say what the check found"
162
+ })
163
+ });
157
164
  var kbAnchorSchema = import_zod.z.object({
158
165
  file: import_zod.z.string().min(1),
159
166
  symbol: import_zod.z.string().min(1).optional()
@@ -238,6 +245,7 @@ var ErrorTypes = /* @__PURE__ */ ((ErrorTypes2) => {
238
245
  ErrorTypes2["KbRecordAlreadyExists"] = "KbRecordAlreadyExists";
239
246
  ErrorTypes2["KbInvalidConceptId"] = "KbInvalidConceptId";
240
247
  ErrorTypes2["KbRecordNotFound"] = "KbRecordNotFound";
248
+ ErrorTypes2["KbSelfVerification"] = "KbSelfVerification";
241
249
  ErrorTypes2["KbWriteConflict"] = "KbWriteConflict";
242
250
  return ErrorTypes2;
243
251
  })(ErrorTypes || {});
@@ -306,6 +314,25 @@ var KbWriteConflictError = class extends BaseError {
306
314
  }
307
315
  conceptId;
308
316
  };
317
+ var KbSelfVerificationError = class extends BaseError {
318
+ constructor(conceptId2, actor, generatedBy) {
319
+ super({
320
+ message: `kb: ${conceptId2} was generated by ${generatedBy}, and a record's generator cannot verify it \u2014 only a human or a different actor can`,
321
+ errorType: "KbSelfVerification" /* KbSelfVerification */,
322
+ code: 400,
323
+ fault: "User" /* User */,
324
+ retriable: false,
325
+ reportToUser: true,
326
+ details: { conceptId: conceptId2, actor, generatedBy, action: "refused" }
327
+ });
328
+ this.conceptId = conceptId2;
329
+ this.actor = actor;
330
+ this.generatedBy = generatedBy;
331
+ }
332
+ conceptId;
333
+ actor;
334
+ generatedBy;
335
+ };
309
336
  var KbInvalidConceptIdError = class extends BaseError {
310
337
  constructor(message, details) {
311
338
  super({
@@ -749,6 +776,40 @@ var KbStore = class {
749
776
  { operation: `status:${status}`, by: actor }
750
777
  );
751
778
  }
779
+ /**
780
+ * Appends one `verified[]` event: who checked the record, when, and what the
781
+ * check found. Append-only — prior events are history, and are spread into
782
+ * the new array untouched rather than reshaped through the write schema.
783
+ *
784
+ * A record's generator cannot verify its own record unless the actor is
785
+ * human: the generator re-reading its own output is not an independent
786
+ * check. The rule runs before the mutation so a refusal never publishes,
787
+ * and the refusal is logged under its own operation name — `mutate` only
788
+ * logs what it publishes.
789
+ */
790
+ async verify(bundlePath2, conceptId2, note, actor = "unknown", at = (/* @__PURE__ */ new Date()).toISOString()) {
791
+ const event = kbVerifiedEventSchema.parse({ by: actor, at, note });
792
+ const existing = await this.read(bundlePath2, conceptId2);
793
+ if (!existing) throw new KbRecordNotFoundError(conceptId2);
794
+ const generatedBy = existing.frontmatter.generated?.by;
795
+ if (generatedBy !== void 0 && actor.toLowerCase() === generatedBy.toLowerCase() && !normalizeActor(actor).startsWith("human:")) {
796
+ await this.record(this.root(bundlePath2), {
797
+ operation: "verify:refused",
798
+ conceptId: conceptId2,
799
+ by: actor
800
+ });
801
+ throw new KbSelfVerificationError(conceptId2, actor, generatedBy);
802
+ }
803
+ return this.mutate(
804
+ bundlePath2,
805
+ conceptId2,
806
+ (frontmatter) => ({
807
+ ...frontmatter,
808
+ verified: [...frontmatter.verified ?? [], event]
809
+ }),
810
+ { operation: "verify", by: actor }
811
+ );
812
+ }
752
813
  /**
753
814
  * Marks `conceptId` superseded by `replacementId`, and links both directions.
754
815
  *
@@ -1083,6 +1144,11 @@ function matches(record, needle) {
1083
1144
  (field) => field?.toLowerCase().includes(needle)
1084
1145
  );
1085
1146
  }
1147
+ function normalizeActor(id) {
1148
+ const colon = id.indexOf(":");
1149
+ if (colon === -1) return id.toLowerCase();
1150
+ return id.slice(0, colon + 1).toLowerCase() + id.slice(colon + 1);
1151
+ }
1086
1152
  function digest(contents) {
1087
1153
  return (0, import_node_crypto.createHash)("sha256").update(contents).digest("hex");
1088
1154
  }
@@ -2400,8 +2466,37 @@ var validateCommand = define({
2400
2466
  failsWhen: (result) => Array.isArray(result) && result.length > 0
2401
2467
  });
2402
2468
 
2403
- // src/commands/write.ts
2469
+ // src/commands/verify.ts
2404
2470
  var import_zod26 = require("zod");
2471
+ var verifyCommand = define({
2472
+ name: "verify",
2473
+ tool: "kb_verify",
2474
+ usage: "verify <concept-id> --note <text>",
2475
+ description: "Append one verified[] event \u2014 who checked the record, when, and what the check found. Appends only; prior events are never rewritten. A record's own generator is refused unless the actor is human: re-reading your own output is not an independent check.",
2476
+ input: import_zod26.z.object({
2477
+ bundlePath,
2478
+ conceptId,
2479
+ note: import_zod26.z.string().refine((s) => s.trim().length > 0, {
2480
+ message: "note must say what the check found"
2481
+ })
2482
+ }),
2483
+ fromArgv: (argv, path) => ({
2484
+ bundlePath: path,
2485
+ conceptId: argv[1],
2486
+ note: argvFlag(argv, "--note")
2487
+ }),
2488
+ run: async ({ store, actor, now }, { bundlePath: path, conceptId: id, note }) => {
2489
+ await assertBaseNotFrozen(process.cwd(), path);
2490
+ const record = await store.verify(path, id, note, actor, now());
2491
+ return {
2492
+ conceptId: record.conceptId,
2493
+ verified: record.frontmatter.verified?.length ?? 0
2494
+ };
2495
+ }
2496
+ });
2497
+
2498
+ // src/commands/write.ts
2499
+ var import_zod27 = require("zod");
2405
2500
  var writeCommand = define({
2406
2501
  name: "write",
2407
2502
  tool: "kb_write",
@@ -2415,9 +2510,9 @@ var writeCommand = define({
2415
2510
  "- Prefer a new record over overloading an existing one, and keep each short. A record nobody finishes reading is not durable memory.",
2416
2511
  "- Records are never deleted; supersede instead, so the earlier reasoning stays inspectable."
2417
2512
  ].join("\n"),
2418
- input: import_zod26.z.object({
2513
+ input: import_zod27.z.object({
2419
2514
  bundlePath,
2420
- type: import_zod26.z.enum(KB_RECORD_TYPES),
2515
+ type: import_zod27.z.enum(KB_RECORD_TYPES),
2421
2516
  input: composeInputSchema
2422
2517
  }),
2423
2518
  fromArgv: async (argv, path, stdin) => ({
@@ -2441,7 +2536,7 @@ var writeCommand = define({
2441
2536
  });
2442
2537
 
2443
2538
  // src/commands/write-decision.ts
2444
- var import_zod27 = require("zod");
2539
+ var import_zod28 = require("zod");
2445
2540
  var writeDecisionCommand = define({
2446
2541
  name: "write-decision",
2447
2542
  tool: "kb_write_decision",
@@ -2454,7 +2549,7 @@ var writeDecisionCommand = define({
2454
2549
  "- `alternative` is what you turned down and why, not a list of everything considered.",
2455
2550
  "- A reference to material you read goes in `sources`; a reference to code goes in `anchors`; a reference to another record goes in `relatedConceptIds`."
2456
2551
  ].join("\n"),
2457
- input: import_zod27.z.object({ bundlePath, input: decisionInputSchema }),
2552
+ input: import_zod28.z.object({ bundlePath, input: decisionInputSchema }),
2458
2553
  fromArgv: async (_argv, path, stdin) => ({
2459
2554
  bundlePath: path,
2460
2555
  input: JSON.parse(await stdin())
@@ -2482,6 +2577,7 @@ var KB_COMMANDS = [
2482
2577
  statusCommand,
2483
2578
  supersedeCommand,
2484
2579
  answerCommand,
2580
+ verifyCommand,
2485
2581
  loadCommand,
2486
2582
  queryCommand,
2487
2583
  traceCommand,
@@ -2645,6 +2741,7 @@ function usage() {
2645
2741
  KbPinsMalformedError,
2646
2742
  KbRecordAlreadyExistsError,
2647
2743
  KbRecordNotFoundError,
2744
+ KbSelfVerificationError,
2648
2745
  KbStore,
2649
2746
  KbWriteConflictError,
2650
2747
  LOG_FILE,
@@ -2675,6 +2772,7 @@ function usage() {
2675
2772
  kbLogEntrySchema,
2676
2773
  kbRecordFrontmatterSchema,
2677
2774
  kbSourceSchema,
2775
+ kbVerifiedEventSchema,
2678
2776
  listPins,
2679
2777
  loadQmd,
2680
2778
  matchToDiff,