@mytegroupinc/myte-core 0.0.55 → 0.0.56

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.
@@ -19,6 +19,7 @@ const LIVE_SCENARIO_MATRIX = [
19
19
  { id: "create-independent-prds", kind: "write", mutation: "test_feedback", required: true },
20
20
  { id: "create-three-document-prd", kind: "write", mutation: "test_feedback", required: true },
21
21
  { id: "feedback-sync", kind: "read/local-sync", mutation: "local_temp_only", required: true },
22
+ { id: "feedback-ticket-batch-lifecycle", kind: "synchronous-governed-write", mutation: "test_feedback_ticket", required: true },
22
23
  { id: "feedback-get-history", kind: "read", mutation: "none", required: true },
23
24
  { id: "feedback-comment", kind: "write", mutation: "test_comment", required: true },
24
25
  { id: "feedback-comment-sync-roundtrip", kind: "read/local-sync", mutation: "local_temp_only", required: true },
@@ -657,6 +658,150 @@ async function main() {
657
658
  const collaboratorWriteKey = collaboratorApiKey || ownerApiKey;
658
659
  const operationalReviewKey = delegateApiKey || ownerApiKey;
659
660
  roleMatrix.collaborator_writes_used = Boolean(collaboratorApiKey);
661
+
662
+ const ticketVersions = runCli([
663
+ "feedback",
664
+ "prd-versions",
665
+ "--feedback-id",
666
+ documentSetFeedbackId,
667
+ "--json",
668
+ ...baseArgs,
669
+ ], workspace, { apiKey: collaboratorWriteKey }).stdout;
670
+ const ticketVersionId = String(
671
+ ticketVersions.active_prd_version_id
672
+ || ticketVersions.versions?.[0]?.version_id
673
+ || ticketVersions.versions?.[0]?._id
674
+ || "",
675
+ );
676
+ const ticketDocumentId = String(documentSetCreate.documents?.[0]?.document_id || "");
677
+ assert(ticketVersionId && ticketDocumentId, "Ticket certification could not resolve a PRD version/document anchor.");
678
+ const ticketPayloadPath = path.join(workspace, `tickets-${runId}.json`);
679
+ const ticketPayload = {
680
+ items: [
681
+ {
682
+ ticket_key: `${namespace}-api`,
683
+ title: `${namespace} API subticket`,
684
+ summary: "Disposable batch lifecycle certification ticket.",
685
+ severity: "High",
686
+ prd_links: [{ version_id: ticketVersionId, document_id: ticketDocumentId }],
687
+ },
688
+ {
689
+ ticket_key: `${namespace}-web`,
690
+ title: `${namespace} Web subticket`,
691
+ summary: "Disposable batch lifecycle certification ticket.",
692
+ severity: "Medium",
693
+ prd_links: [{ version_id: ticketVersionId, document_id: ticketDocumentId }],
694
+ },
695
+ ],
696
+ };
697
+ fs.writeFileSync(ticketPayloadPath, `${JSON.stringify(ticketPayload, null, 2)}\n`, "utf8");
698
+ const ticketApprovalPath = path.join(workspace, `tickets-${runId}-approval.json`);
699
+ fs.writeFileSync(ticketApprovalPath, `${JSON.stringify({
700
+ command: "myte feedback tickets create",
701
+ reason,
702
+ feedback_id: documentSetFeedbackId,
703
+ batch_count: 2,
704
+ tickets: ticketPayload.items,
705
+ }, null, 2)}\n`, "utf8");
706
+ const ticketValidation = runCli([
707
+ "feedback", "tickets", "create",
708
+ "--feedback-id", documentSetFeedbackId,
709
+ "--file", ticketPayloadPath,
710
+ "--dry-run", "--json", ...baseArgs,
711
+ ], workspace, { apiKey: collaboratorWriteKey }).stdout;
712
+ assert(ticketValidation.valid === true && ticketValidation.item_count === 2, "Ticket batch validation did not certify both items.");
713
+ const ticketCreate = runCli([
714
+ "feedback", "tickets", "create",
715
+ "--feedback-id", documentSetFeedbackId,
716
+ "--file", ticketPayloadPath,
717
+ "--confirm-write", "--approval-artifact", ticketApprovalPath,
718
+ "--idempotency-key", `ticket-create-${runId}`,
719
+ "--json", ...baseArgs,
720
+ ], workspace, { apiKey: collaboratorWriteKey }).stdout;
721
+ assert(ticketCreate.created_count === 2 && ticketCreate.receipts?.length === 2, "Ticket batch create did not return two receipts.");
722
+ const ticketIds = ticketCreate.receipts.map((receipt) => String(receipt.ticket_id));
723
+ for (const ticketId of ticketIds) {
724
+ recordCertificationArtifact(manifest, {
725
+ kind: "feedback_ticket",
726
+ id: ticketId,
727
+ metadata: { feedback_id: documentSetFeedbackId, cleanup: "retained_with_archived_parent" },
728
+ });
729
+ }
730
+ if (ticketCreate.batch_id) {
731
+ recordCertificationArtifact(manifest, {
732
+ kind: "ticket_batch",
733
+ id: String(ticketCreate.batch_id),
734
+ metadata: { operation: "create", feedback_id: documentSetFeedbackId },
735
+ });
736
+ }
737
+ const ticketUpdateApprovalPath = path.join(workspace, `tickets-${runId}-update.json`);
738
+ fs.writeFileSync(ticketUpdateApprovalPath, `${JSON.stringify({
739
+ command: "myte feedback tickets update",
740
+ reason,
741
+ feedback_id: documentSetFeedbackId,
742
+ ticket_ids: ticketIds,
743
+ batch_count: ticketIds.length,
744
+ proposed_change: { status: "in_progress" },
745
+ }, null, 2)}\n`, "utf8");
746
+ const ticketUpdate = runCli([
747
+ "feedback", "tickets", "update",
748
+ "--feedback-id", documentSetFeedbackId,
749
+ "--ticket-ids", ticketIds.join(","),
750
+ "--status", "in_progress",
751
+ "--confirm-write", "--approval-artifact", ticketUpdateApprovalPath,
752
+ "--idempotency-key", `ticket-update-${runId}`,
753
+ "--json", ...baseArgs,
754
+ ], workspace, { apiKey: collaboratorWriteKey }).stdout;
755
+ assert(ticketUpdate.updated_count === 2, "Ticket batch update did not update both items.");
756
+ if (ticketUpdate.batch_id) {
757
+ recordCertificationArtifact(manifest, {
758
+ kind: "ticket_batch",
759
+ id: String(ticketUpdate.batch_id),
760
+ metadata: { operation: "update", feedback_id: documentSetFeedbackId },
761
+ });
762
+ }
763
+ const ticketList = runCli([
764
+ "feedback", "tickets", "list",
765
+ "--feedback-id", documentSetFeedbackId,
766
+ "--json", ...baseArgs,
767
+ ], workspace, { apiKey: collaboratorWriteKey }).stdout;
768
+ assert(
769
+ ticketIds.every((ticketId) => ticketList.tickets?.some((ticket) => String(ticket._id || ticket.ticket_id) === ticketId && ticket.status === "in_progress")),
770
+ "Ticket list did not reflect the batch lifecycle update.",
771
+ );
772
+ const ticketCoverageApprovalPath = path.join(workspace, `tickets-${runId}-coverage.json`);
773
+ fs.writeFileSync(ticketCoverageApprovalPath, `${JSON.stringify({
774
+ command: "myte feedback tickets coverage",
775
+ reason,
776
+ feedback_id: documentSetFeedbackId,
777
+ ticket_id: ticketIds[0],
778
+ version_id: ticketVersionId,
779
+ document_id: ticketDocumentId,
780
+ coverage_status: "partially_covered",
781
+ }, null, 2)}\n`, "utf8");
782
+ const ticketCoverage = runCli([
783
+ "feedback", "tickets", "coverage",
784
+ "--feedback-id", documentSetFeedbackId,
785
+ "--ticket-id", ticketIds[0],
786
+ "--version-id", ticketVersionId,
787
+ "--document-id", ticketDocumentId,
788
+ "--coverage-status", "partially_covered",
789
+ "--confirm-write", "--approval-artifact", ticketCoverageApprovalPath,
790
+ "--idempotency-key", `ticket-coverage-${runId}`,
791
+ "--json", ...baseArgs,
792
+ ], workspace, { apiKey: collaboratorWriteKey }).stdout;
793
+ assert(
794
+ ticketCoverage.prd_links?.some((link) => String(link.version_id) === ticketVersionId && String(link.document_id) === ticketDocumentId && link.coverage_status === "partially_covered"),
795
+ "Ticket coverage update did not persist on the expected PRD link.",
796
+ );
797
+ runCli(["feedback-sync", "--json", ...baseArgs], workspace, { apiKey: collaboratorWriteKey });
798
+ const syncedTicketItem = findFeedbackItem(workspace, documentSetFeedbackId);
799
+ assert(
800
+ syncedTicketItem?.tickets?.length >= 2 && syncedTicketItem.ticket_count >= 2,
801
+ "feedback-sync did not hydrate ticket summaries.",
802
+ );
803
+ writeCertificationManifest(manifestPath, manifest);
804
+
660
805
  const commentApprovalPath = writeCommentApproval(
661
806
  workspace,
662
807
  runId,
@@ -8,6 +8,7 @@ const { spawnSync } = require("node:child_process");
8
8
 
9
9
  const CLI_PATH = path.resolve(__dirname, "..", "cli.js");
10
10
  const DEFAULT_BASE_URL = "https://api.myte.dev/api";
11
+ const PACKAGE_VERSION = require("../package.json").version;
11
12
 
12
13
  function parseArgs(argv) {
13
14
  const args = { _: [] };
@@ -285,7 +286,7 @@ class Harness {
285
286
  this.key = getProjectKey();
286
287
  this.actorBase = String(args["actor-scope"] || `mission-live-full-${nowId()}`);
287
288
  this.cliMode = String(args.cli || "local").toLowerCase();
288
- this.packageVersion = String(args["package-version"] || "0.0.28");
289
+ this.packageVersion = String(args["package-version"] || PACKAGE_VERSION);
289
290
  this.batchSize = Math.max(1, Math.min(10, Number(args["batch-size"] || 10)));
290
291
  this.includeQaqcRun = args["include-qaqc-run"] === true;
291
292
  this.workspace = path.resolve(
@@ -433,7 +434,7 @@ class Harness {
433
434
  change_set: { description: "Harness proposed description. This should be rejected during cleanup." },
434
435
  }],
435
436
  });
436
- const first = this.cli(["suggestions", "create", "--file", createOne, "--no-sync", "--json"], { actorScope: actor });
437
+ const first = this.cli(["suggestions", "create", "--file", createOne, "--confirm-write", "--approval-artifact", createOne, "--no-sync", "--json"], { actorScope: actor });
437
438
  const suggestionId = pickSuggestionId(first);
438
439
  assertCondition(suggestionId, "Update suggestion create did not return suggestion_id.");
439
440
  this.openSuggestions.add(suggestionId);
@@ -446,7 +447,7 @@ class Harness {
446
447
  change_set: { acceptance_criteria: ["Harness proposed acceptance criterion. This should be rejected."] },
447
448
  }],
448
449
  });
449
- const second = this.cli(["suggestions", "create", "--file", createTwo, "--no-sync", "--json"], { actorScope: actor });
450
+ const second = this.cli(["suggestions", "create", "--file", createTwo, "--confirm-write", "--approval-artifact", createTwo, "--no-sync", "--json"], { actorScope: actor });
450
451
  const appendedId = pickSuggestionId(second);
451
452
  assertCondition(appendedId === suggestionId, `Expected append to same suggestion ${suggestionId}, got ${appendedId || "(none)"}.`);
452
453
 
@@ -457,7 +458,7 @@ class Harness {
457
458
  change_set: { technical_requirements: ["Harness proposed requirement. This should be rejected."] },
458
459
  }],
459
460
  });
460
- const revised = this.cli(["suggestions", "revise", "--file", revise, "--no-sync", "--json"], { actorScope: actor });
461
+ const revised = this.cli(["suggestions", "revise", "--file", revise, "--confirm-write", "--approval-artifact", revise, "--no-sync", "--json"], { actorScope: actor });
461
462
  assertCondition((revised.revised_count || 0) >= 1, "Update suggestion revise did not revise the thread.");
462
463
 
463
464
  const requestChanges = writeJson(this.payloadDir, "update-loop-request-changes.json", {
@@ -473,7 +474,7 @@ class Harness {
473
474
  change_set: { labels: ["harness-proposed-update"] },
474
475
  }],
475
476
  });
476
- const revisedAgain = this.cli(["suggestions", "revise", "--file", reviseAfterRequest, "--no-sync", "--json"], { actorScope: actor });
477
+ const revisedAgain = this.cli(["suggestions", "revise", "--file", reviseAfterRequest, "--confirm-write", "--approval-artifact", reviseAfterRequest, "--no-sync", "--json"], { actorScope: actor });
477
478
  assertCondition((revisedAgain.revised_count || 0) >= 1, "Update suggestion revise after request_changes did not process.");
478
479
 
479
480
  const reject = writeJson(this.payloadDir, "update-loop-reject.json", {
@@ -504,7 +505,7 @@ class Harness {
504
505
  },
505
506
  }],
506
507
  });
507
- const created = this.cli(["suggestions", "create", "--file", createFile, "--no-sync", "--json"], { actorScope: actor });
508
+ const created = this.cli(["suggestions", "create", "--file", createFile, "--confirm-write", "--approval-artifact", createFile, "--no-sync", "--json"], { actorScope: actor });
508
509
  const suggestionId = pickSuggestionId(created);
509
510
  assertCondition(suggestionId, "Create suggestion did not return suggestion_id.");
510
511
  this.openSuggestions.add(suggestionId);
@@ -522,7 +523,7 @@ class Harness {
522
523
  change_set: { description: "Harness revised proposal. This should still be rejected." },
523
524
  }],
524
525
  });
525
- const revised = this.cli(["suggestions", "revise", "--file", reviseFile, "--no-sync", "--json"], { actorScope: actor });
526
+ const revised = this.cli(["suggestions", "revise", "--file", reviseFile, "--confirm-write", "--approval-artifact", reviseFile, "--no-sync", "--json"], { actorScope: actor });
526
527
  assertCondition((revised.revised_count || 0) >= 1, "Create suggestion revise did not process.");
527
528
 
528
529
  const requestChanges = writeJson(this.payloadDir, "create-reject-request-changes.json", {
@@ -579,7 +580,7 @@ class Harness {
579
580
  },
580
581
  }],
581
582
  });
582
- const created = this.cli(["suggestions", "create", "--file", createFile, "--no-sync", "--json"], { actorScope: actor, timeoutMs: 180000 });
583
+ const created = this.cli(["suggestions", "create", "--file", createFile, "--confirm-write", "--approval-artifact", createFile, "--no-sync", "--json"], { actorScope: actor, timeoutMs: 180000 });
583
584
  const suggestionId = pickSuggestionId(created);
584
585
  assertCondition(suggestionId, "Approved mission create did not return suggestion_id.");
585
586
  this.openSuggestions.add(suggestionId);
@@ -633,7 +634,7 @@ class Harness {
633
634
  change_set: { description: "Harness pending suggestion should be hidden from actionable queue while archived." },
634
635
  }],
635
636
  });
636
- const pending = this.cli(["suggestions", "create", "--file", pendingFile, "--no-sync", "--json"], { actorScope: actor, timeoutMs: 180000 });
637
+ const pending = this.cli(["suggestions", "create", "--file", pendingFile, "--confirm-write", "--approval-artifact", pendingFile, "--no-sync", "--json"], { actorScope: actor, timeoutMs: 180000 });
637
638
  pendingSuggestionId = pickSuggestionId(pending);
638
639
  assertCondition(pendingSuggestionId, "Pending update suggestion for approved mission was not created.");
639
640
  this.openSuggestions.add(pendingSuggestionId);