@metasession.co/devaudit-cli 0.3.11 → 0.3.13

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.
Files changed (68) hide show
  1. package/dist/index.js +94 -6
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -2
  4. package/scripts/upload-evidence.sh +55 -1
  5. package/sdlc/ai-rules/SDLC_RULES.md +27 -7
  6. package/sdlc/files/_common/0-project-setup.md +13 -7
  7. package/sdlc/files/_common/Periodic_Security_Review_Schedule.md +5 -5
  8. package/sdlc/files/_common/README_TEMPLATE.md +8 -8
  9. package/sdlc/files/_common/Test_Architecture.md +15 -15
  10. package/sdlc/files/_common/Test_Plan_TEMPLATE.md +13 -13
  11. package/sdlc/files/_common/Test_Policy.md +2 -2
  12. package/sdlc/files/_common/Test_Strategy.md +10 -10
  13. package/sdlc/files/_common/governance/ai-disclosure.md.template +3 -3
  14. package/sdlc/files/_common/governance/dpia.md.template +3 -3
  15. package/sdlc/files/_common/governance/incident-report.md.template +3 -3
  16. package/sdlc/files/_common/governance/nil-incident-report.md.template +1 -1
  17. package/sdlc/files/_common/governance/periodic-review.md.template +3 -3
  18. package/sdlc/files/_common/governance/risk-register.md.template +1 -1
  19. package/sdlc/files/_common/governance/ropa.md.template +2 -2
  20. package/sdlc/files/_common/joining-an-existing-project.md +4 -4
  21. package/sdlc/files/_common/scripts/check-host-deployment.sh +107 -0
  22. package/sdlc/files/_common/scripts/check-host-deployment.test.sh +119 -0
  23. package/sdlc/files/_common/scripts/check-release-pr-scope.sh +95 -0
  24. package/sdlc/files/_common/scripts/check-release-pr-scope.test.sh +137 -0
  25. package/sdlc/files/_common/scripts/close-out-release.sh +175 -54
  26. package/sdlc/files/_common/scripts/close-out-release.test.sh +90 -0
  27. package/sdlc/files/_common/scripts/extract-release-metadata.sh +65 -0
  28. package/sdlc/files/_common/scripts/extract-release-metadata.test.sh +38 -0
  29. package/sdlc/files/_common/scripts/generate-bundled-changes.sh +307 -58
  30. package/sdlc/files/_common/scripts/generate-bundled-changes.test.sh +128 -78
  31. package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.sh +18 -9
  32. package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.test.sh +66 -0
  33. package/sdlc/files/_common/scripts/generate-security-summary.sh +14 -5
  34. package/sdlc/files/_common/scripts/generate-security-summary.test.sh +57 -0
  35. package/sdlc/files/_common/scripts/prepare-release-pr.sh +44 -4
  36. package/sdlc/files/_common/scripts/render-test-cycles.sh +227 -0
  37. package/sdlc/files/_common/scripts/render-test-cycles.test.sh +107 -0
  38. package/sdlc/files/_common/scripts/report-test-cycle.sh +344 -0
  39. package/sdlc/files/_common/scripts/submit-bundle-manifest.sh +100 -0
  40. package/sdlc/files/_common/scripts/submit-bundle-manifest.test.sh +138 -0
  41. package/sdlc/files/_common/scripts/upload-evidence.sh +55 -1
  42. package/sdlc/files/_common/scripts/upload-evidence.test.sh +119 -0
  43. package/sdlc/files/_common/scripts/validate-compliance-artifacts.sh +169 -2
  44. package/sdlc/files/_common/scripts/validate-compliance-artifacts.test.sh +169 -0
  45. package/sdlc/files/_common/scripts/validate-test-summary.test.sh +4 -4
  46. package/sdlc/files/_common/skills/adr-author/SKILL.md +2 -2
  47. package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +2 -2
  48. package/sdlc/files/_common/skills/e2e-test-engineer/references/bootstrap.md +5 -5
  49. package/sdlc/files/_common/skills/governance-doc-author/references/incident-classification.md +1 -1
  50. package/sdlc/files/_common/skills/requirements-aligner/SKILL.md +4 -4
  51. package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +57 -3
  52. package/sdlc/files/_common/skills/sdlc-implementer/references/change-request-loop.md +1 -1
  53. package/sdlc/files/_common/skills/sdlc-implementer/references/compliance-constraints.md +7 -14
  54. package/sdlc/files/ci/check-release-approval.yml.template +18 -28
  55. package/sdlc/files/ci/ci.yml.template +95 -35
  56. package/sdlc/files/ci/close-out-release.yml.template +7 -4
  57. package/sdlc/files/ci/compliance-evidence.yml.template +234 -49
  58. package/sdlc/files/ci/feature-e2e.yml.template +84 -1
  59. package/sdlc/files/ci/post-deploy-prod.yml.template +59 -4
  60. package/sdlc/files/ci/python/ci.yml.template +60 -12
  61. package/sdlc/files/ci/quality-gates-provenance.yml.template +123 -28
  62. package/sdlc/package.json +1 -1
  63. package/sdlc/src/bin/devaudit-sdlc.js +44 -2
  64. package/sdlc/src/blueprints/1-plan-requirement.raw.md +6 -1
  65. package/sdlc/src/blueprints/2-implement-and-test.raw.md +1 -1
  66. package/sdlc/src/blueprints/3-compile-evidence.raw.md +102 -20
  67. package/sdlc/src/blueprints/4-submit-for-review.raw.md +3 -3
  68. package/sdlc/src/blueprints/5-deploy-main.raw.md +2 -2
package/dist/index.js CHANGED
@@ -56,7 +56,7 @@ function emitJsonResult(payload) {
56
56
 
57
57
  // package.json
58
58
  var package_default = {
59
- version: "0.3.11"};
59
+ version: "0.3.13"};
60
60
 
61
61
  // src/lib/version.ts
62
62
  var CLI_VERSION = package_default.version;
@@ -595,6 +595,23 @@ async function runStatus(options) {
595
595
  log.warn(`${missingFiles.length} framework file(s) missing. Re-sync with \`devaudit update <version> <path>\` to refresh.`);
596
596
  }
597
597
  }
598
+
599
+ // src/lib/release-lineage-contract.ts
600
+ function renderEvidenceLineageFields(input, capabilities) {
601
+ if (input.testCycleRecordId && input.evidenceScope !== "cycle") {
602
+ throw new Error("testCycleRecordId requires cycle evidenceScope");
603
+ }
604
+ if (!capabilities.supportsFirstClassCycleApi) {
605
+ return input.testCycleId ? { testCycleId: input.testCycleId } : {};
606
+ }
607
+ return {
608
+ ...input.evidenceScope ? { evidenceScope: input.evidenceScope } : {},
609
+ ...input.testCycleRecordId ? { testCycleRecordId: input.testCycleRecordId } : {},
610
+ ...input.testCycleId ? { testCycleId: input.testCycleId } : {}
611
+ };
612
+ }
613
+
614
+ // src/lib/ci-upload.ts
598
615
  var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
599
616
  var DEFAULT_MAX_ATTEMPTS = 5;
600
617
  var DEFAULT_UPLOAD_MAX_TIME_SECONDS = 120;
@@ -685,12 +702,24 @@ async function createUploadSource(file) {
685
702
  }
686
703
  }
687
704
  function buildUploadForm(file, source, opts) {
705
+ const metadata = {
706
+ ...opts.metadata ?? {},
707
+ ...opts.commitTimestamp ? { commitTimestamp: opts.commitTimestamp } : {}
708
+ };
709
+ const lineageFields = renderEvidenceLineageFields(
710
+ {
711
+ evidenceScope: opts.evidenceScope,
712
+ testCycleRecordId: opts.testCycleRecordId,
713
+ testCycleId: opts.testCycleId
714
+ },
715
+ opts.lineageCapabilities ?? { supportsFirstClassCycleApi: true }
716
+ );
688
717
  const form = new FormData();
689
718
  form.set("file", source.blob, basename(file));
690
719
  form.set("projectSlug", opts.projectSlug);
691
720
  form.set("requirementId", opts.requirementId);
692
721
  form.set("evidenceType", opts.evidenceType);
693
- form.set("metadata", JSON.stringify(opts.metadata ?? {}));
722
+ form.set("metadata", JSON.stringify(metadata));
694
723
  if (opts.releaseVersion) form.set("releaseVersion", opts.releaseVersion);
695
724
  if (opts.createReleaseIfMissing) form.set("createReleaseIfMissing", "true");
696
725
  if (opts.environment) form.set("environment", opts.environment);
@@ -701,7 +730,11 @@ function buildUploadForm(file, source, opts) {
701
730
  if (opts.changeType) form.set("changeType", opts.changeType);
702
731
  if (opts.gateStatus) form.set("gateStatus", opts.gateStatus);
703
732
  if (opts.sdlcStage) form.set("sdlcStage", opts.sdlcStage);
704
- if (opts.testCycleId) form.set("testCycleId", opts.testCycleId);
733
+ if (lineageFields.testCycleId) form.set("testCycleId", lineageFields.testCycleId);
734
+ if (lineageFields.evidenceScope) form.set("evidenceScope", lineageFields.evidenceScope);
735
+ if (lineageFields.testCycleRecordId) form.set("testCycleRecordId", lineageFields.testCycleRecordId);
736
+ if (opts.sentinelContent) form.set("sentinelContent", opts.sentinelContent);
737
+ if (opts.commitTimestamp) form.set("commitTimestamp", opts.commitTimestamp);
705
738
  return form;
706
739
  }
707
740
  function uploadFailureMessage(err, timeoutSeconds) {
@@ -786,7 +819,18 @@ async function uploadPresigned(file, source, opts) {
786
819
  const maxAttemptsPresigned = DEFAULT_PRESIGNED_MAX_ATTEMPTS;
787
820
  const timeoutSeconds = uploadMaxTimeSeconds();
788
821
  const presignedTimeoutSeconds = DEFAULT_PRESIGNED_UPLOAD_MAX_TIME_SECONDS;
789
- const metadata = opts.metadata ?? {};
822
+ const metadata = {
823
+ ...opts.metadata ?? {},
824
+ ...opts.commitTimestamp ? { commitTimestamp: opts.commitTimestamp } : {}
825
+ };
826
+ const lineageFields = renderEvidenceLineageFields(
827
+ {
828
+ evidenceScope: opts.evidenceScope,
829
+ testCycleRecordId: opts.testCycleRecordId,
830
+ testCycleId: opts.testCycleId
831
+ },
832
+ opts.lineageCapabilities ?? { supportsFirstClassCycleApi: true }
833
+ );
790
834
  let uploadUrl = "";
791
835
  let evidenceId = "";
792
836
  let attempt = 1;
@@ -817,8 +861,13 @@ async function uploadPresigned(file, source, opts) {
817
861
  ...opts.evidenceCategory ? { evidenceCategory: opts.evidenceCategory } : {},
818
862
  ...opts.releaseTitle ? { releaseTitle: opts.releaseTitle } : {},
819
863
  ...opts.releaseSummary ? { releaseSummary: opts.releaseSummary } : {},
864
+ ...opts.changeType ? { changeType: opts.changeType } : {},
820
865
  ...opts.sdlcStage ? { sdlcStage: opts.sdlcStage } : {},
821
- ...opts.testCycleId ? { testCycleId: opts.testCycleId } : {}
866
+ ...lineageFields.testCycleId ? { testCycleId: lineageFields.testCycleId } : {},
867
+ ...lineageFields.evidenceScope ? { evidenceScope: lineageFields.evidenceScope } : {},
868
+ ...lineageFields.testCycleRecordId ? { testCycleRecordId: lineageFields.testCycleRecordId } : {},
869
+ ...opts.sentinelContent ? { sentinelContent: opts.sentinelContent } : {},
870
+ ...opts.commitTimestamp ? { commitTimestamp: opts.commitTimestamp } : {}
822
871
  }),
823
872
  signal: controller.signal
824
873
  });
@@ -964,6 +1013,7 @@ async function uploadEvidence(opts) {
964
1013
 
965
1014
  // src/commands/push.ts
966
1015
  var DEFAULT_BASE_URL3 = "https://devaudit.metasession.co";
1016
+ var TRACKED_CHANGE_TYPES = /* @__PURE__ */ new Set(["feat", "fix", "refactor", "perf", "compliance", "revert"]);
967
1017
  function buildMetadata(options) {
968
1018
  const metadata = {};
969
1019
  if (options.gitSha) metadata["gitSha"] = options.gitSha;
@@ -975,6 +1025,28 @@ function buildMetadata(options) {
975
1025
  }
976
1026
  return metadata;
977
1027
  }
1028
+ async function resolveSentinelContext(options) {
1029
+ if (!options.changeType || !TRACKED_CHANGE_TYPES.has(options.changeType)) {
1030
+ return {};
1031
+ }
1032
+ const sentinelPath = join(process.cwd(), ".sdlc-implementer-invoked");
1033
+ let sentinelContent;
1034
+ try {
1035
+ const raw = await promises.readFile(sentinelPath, "utf8");
1036
+ if (raw.trim()) sentinelContent = raw;
1037
+ } catch {
1038
+ sentinelContent = void 0;
1039
+ }
1040
+ let commitTimestamp;
1041
+ try {
1042
+ const target = options.gitSha?.trim() ? options.gitSha.trim() : "HEAD";
1043
+ const { stdout } = await execa("git", ["show", "-s", "--format=%cI", target], { reject: false });
1044
+ if (stdout.trim()) commitTimestamp = stdout.trim();
1045
+ } catch {
1046
+ commitTimestamp = void 0;
1047
+ }
1048
+ return { sentinelContent, commitTimestamp };
1049
+ }
978
1050
  function validateOptions(options) {
979
1051
  if (options.environment && !options.release) {
980
1052
  return "--environment requires --release (evidence without a release is orphaned)";
@@ -985,6 +1057,12 @@ function validateOptions(options) {
985
1057
  for (const kv of options.metaKeys ?? []) {
986
1058
  if (!kv.includes("=")) return `--meta-key requires key=value (got: ${kv})`;
987
1059
  }
1060
+ if (options.evidenceScope !== void 0 && !["release", "stage", "cycle", "approval"].includes(options.evidenceScope)) {
1061
+ return "--evidence-scope must be one of: release, stage, cycle, approval";
1062
+ }
1063
+ if (options.testCycleRecordId && options.evidenceScope !== "cycle") {
1064
+ return "--test-cycle-record-id requires --evidence-scope cycle";
1065
+ }
988
1066
  return null;
989
1067
  }
990
1068
  async function runDryRun(options, baseUrl) {
@@ -1045,6 +1123,8 @@ async function runPush(options) {
1045
1123
  await runHook(plugins, "beforePush", ctx);
1046
1124
  }
1047
1125
  const metadata = buildMetadata(options);
1126
+ const { sentinelContent, commitTimestamp } = await resolveSentinelContext(options);
1127
+ if (commitTimestamp) metadata["commitTimestamp"] = commitTimestamp;
1048
1128
  const results = await uploadEvidence({
1049
1129
  projectSlug: options.projectSlug,
1050
1130
  requirementId: options.requirementId,
@@ -1063,6 +1143,10 @@ async function runPush(options) {
1063
1143
  ...options.gateStatus !== void 0 ? { gateStatus: options.gateStatus } : {},
1064
1144
  ...options.sdlcStage !== void 0 ? { sdlcStage: options.sdlcStage } : {},
1065
1145
  ...options.testCycleId !== void 0 ? { testCycleId: options.testCycleId } : {},
1146
+ ...options.evidenceScope !== void 0 ? { evidenceScope: options.evidenceScope } : {},
1147
+ ...options.testCycleRecordId !== void 0 ? { testCycleRecordId: options.testCycleRecordId } : {},
1148
+ ...sentinelContent !== void 0 ? { sentinelContent } : {},
1149
+ ...commitTimestamp !== void 0 ? { commitTimestamp } : {},
1066
1150
  metadata
1067
1151
  });
1068
1152
  let okCount = 0;
@@ -2447,6 +2531,8 @@ async function syncCiTemplates(ctx) {
2447
2531
  const tokens = {
2448
2532
  PROJECT_SLUG: cfg.project_slug,
2449
2533
  PRODUCTION_URL_SECRET: cfg.production_url_secret,
2534
+ INTEGRATION_BRANCH: cfg.integration_branch ?? "develop",
2535
+ RELEASE_BRANCH: cfg.release_branch ?? "main",
2450
2536
  NODE_VERSION: String(cfg.node_version ?? ""),
2451
2537
  PYTHON_VERSION: String(cfg.python_version ?? ""),
2452
2538
  WORKING_DIRECTORY: workingDirectory || ".",
@@ -3388,7 +3474,7 @@ async function main(argv) {
3388
3474
  ...globals.dryRun !== void 0 ? { dryRun: Boolean(globals.dryRun) } : {}
3389
3475
  });
3390
3476
  });
3391
- program.command("push <project-slug> <requirement-id> <evidence-type> <file>").description("Upload evidence file(s) to DevAudit (port of scripts/upload-evidence.sh)").option("--release <version>", "release version (e.g. v1.0.0)").option("--create-release-if-missing", "auto-create the release as 'draft' if absent").option("--environment <env>", "uat | production").option("--category <cat>", "ci_pipeline | local_dev | planning | test_report | security_scan | release_artifact").option("--git-sha <sha>", "attached to metadata.gitSha").option("--ci-run-id <id>", "attached to metadata.ciRunId").option("--branch <name>", "git branch \u2014 sent as releaseBranch + metadata.branch").option("--release-title <text>", "human title for the release row (releaseTitle; portal no-clobbers)").option("--release-summary <text>", "reviewer-facing short description (releaseSummary; portal no-clobbers)").option("--change-type <type>", "conventional-commit prefix for the release row (changeType)").option("--gate-status <status>", "passed | failed | skipped (gateStatus)").option("--sdlc-stage <stage>", "SDLC stage 1-5 (sdlcStage)").option("--test-cycle <id>", "test cycle identifier (typically the CI run ID)").option(
3477
+ program.command("push <project-slug> <requirement-id> <evidence-type> <file>").description("Upload evidence file(s) to DevAudit (port of scripts/upload-evidence.sh)").option("--release <version>", "release version (e.g. v1.0.0)").option("--create-release-if-missing", "auto-create the release as 'draft' if absent").option("--environment <env>", "uat | production").option("--category <cat>", "ci_pipeline | local_dev | planning | test_report | security_scan | release_artifact").option("--git-sha <sha>", "attached to metadata.gitSha").option("--ci-run-id <id>", "attached to metadata.ciRunId").option("--branch <name>", "git branch \u2014 sent as releaseBranch + metadata.branch").option("--release-title <text>", "human title for the release row (releaseTitle; portal no-clobbers)").option("--release-summary <text>", "reviewer-facing short description (releaseSummary; portal no-clobbers)").option("--change-type <type>", "conventional-commit prefix for the release row (changeType)").option("--gate-status <status>", "passed | failed | skipped (gateStatus)").option("--sdlc-stage <stage>", "SDLC stage 1-5 (sdlcStage)").option("--test-cycle <id>", "test cycle identifier (typically the CI run ID)").option("--evidence-scope <scope>", "release | stage | cycle | approval (evidenceScope)").option("--test-cycle-record-id <id>", "first-class portal cycle UUID (requires --evidence-scope cycle)").option(
3392
3478
  "--meta-key <pair>",
3393
3479
  "repeatable key=value merged into the metadata JSON",
3394
3480
  (val, acc) => [...acc, val],
@@ -3414,6 +3500,8 @@ async function main(argv) {
3414
3500
  ...opts.gateStatus !== void 0 ? { gateStatus: opts.gateStatus } : {},
3415
3501
  ...opts.sdlcStage !== void 0 ? { sdlcStage: opts.sdlcStage } : {},
3416
3502
  ...opts.testCycle !== void 0 ? { testCycleId: opts.testCycle } : {},
3503
+ ...opts.evidenceScope !== void 0 ? { evidenceScope: opts.evidenceScope } : {},
3504
+ ...opts.testCycleRecordId !== void 0 ? { testCycleRecordId: opts.testCycleRecordId } : {},
3417
3505
  ...opts.metaKey !== void 0 && opts.metaKey.length > 0 ? { metaKeys: opts.metaKey } : {},
3418
3506
  ...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {},
3419
3507
  ...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},