@cejel/cejel 0.1.1 → 0.1.2

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.
@@ -29,7 +29,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
29
  mod
30
30
  ));
31
31
 
32
- // src/witan/external-findings.ts
32
+ // ../witan/src/external-findings.ts
33
33
  var EXTERNAL_FINDINGS_DISPLAY_LIMIT = 10;
34
34
  var SEVERITY_RANK = {
35
35
  critical: 0,
@@ -82,7 +82,7 @@ function collectExternalFindings(consumedSignals) {
82
82
  });
83
83
  }
84
84
 
85
- // src/witan/html.ts
85
+ // ../witan/src/html.ts
86
86
  function renderWitanHtmlReport(report) {
87
87
  const codeCriteria = criteriaByCategory(report, "code_trust");
88
88
  const processCriteria = criteriaByCategory(report, "process_trust");
@@ -403,7 +403,7 @@ a:hover { text-decoration: underline; text-underline-offset: 2px; }
403
403
  }
404
404
  `;
405
405
 
406
- // src/witan/badge.ts
406
+ // ../witan/src/badge.ts
407
407
  var VERDICT_COLOR = {
408
408
  Verified: "brightgreen",
409
409
  Conditional: "yellow",
@@ -470,7 +470,15 @@ function escapeXml(value) {
470
470
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
471
471
  }
472
472
 
473
- // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
473
+ // ../witan/src/claim-reality.ts
474
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
475
+ import { join } from "path";
476
+
477
+ // ../witan/src/leaderboard.ts
478
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "fs";
479
+ import { join as join6 } from "path";
480
+
481
+ // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
474
482
  var external_exports = {};
475
483
  __export(external_exports, {
476
484
  BRAND: () => BRAND,
@@ -582,7 +590,7 @@ __export(external_exports, {
582
590
  void: () => voidType
583
591
  });
584
592
 
585
- // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js
593
+ // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js
586
594
  var util;
587
595
  (function(util2) {
588
596
  util2.assertEqual = (_) => {
@@ -716,7 +724,7 @@ var getParsedType = (data) => {
716
724
  }
717
725
  };
718
726
 
719
- // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js
727
+ // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js
720
728
  var ZodIssueCode = util.arrayToEnum([
721
729
  "invalid_type",
722
730
  "invalid_literal",
@@ -834,7 +842,7 @@ ZodError.create = (issues) => {
834
842
  return error;
835
843
  };
836
844
 
837
- // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js
845
+ // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js
838
846
  var errorMap = (issue, _ctx) => {
839
847
  let message;
840
848
  switch (issue.code) {
@@ -937,7 +945,7 @@ var errorMap = (issue, _ctx) => {
937
945
  };
938
946
  var en_default = errorMap;
939
947
 
940
- // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js
948
+ // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js
941
949
  var overrideErrorMap = en_default;
942
950
  function setErrorMap(map) {
943
951
  overrideErrorMap = map;
@@ -946,7 +954,7 @@ function getErrorMap() {
946
954
  return overrideErrorMap;
947
955
  }
948
956
 
949
- // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
957
+ // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
950
958
  var makeIssue = (params) => {
951
959
  const { data, path, errorMaps, issueData } = params;
952
960
  const fullPath = [...path, ...issueData.path || []];
@@ -1056,14 +1064,14 @@ var isDirty = (x) => x.status === "dirty";
1056
1064
  var isValid = (x) => x.status === "valid";
1057
1065
  var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
1058
1066
 
1059
- // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
1067
+ // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
1060
1068
  var errorUtil;
1061
1069
  (function(errorUtil2) {
1062
1070
  errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
1063
1071
  errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
1064
1072
  })(errorUtil || (errorUtil = {}));
1065
1073
 
1066
- // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
1074
+ // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
1067
1075
  var ParseInputLazyPath = class {
1068
1076
  constructor(parent, value, path, key) {
1069
1077
  this._cachedPath = [];
@@ -4511,10 +4519,853 @@ var coerce = {
4511
4519
  };
4512
4520
  var NEVER = INVALID;
4513
4521
 
4514
- // src/witan/schemas.ts
4522
+ // ../shared/src/types/attestation.ts
4523
+ var attestationOutcomeSchema = external_exports.enum([
4524
+ "ATTESTED_READY",
4525
+ "ATTESTED_WITH_GAPS",
4526
+ "NOT_READY",
4527
+ "REVOKED"
4528
+ ]);
4529
+ var attestationKindSchema = external_exports.enum(["OPERATOR_READINESS", "EXTERNAL_ASSESSOR_REFERENCE"]);
4530
+ var attestationEvidenceRefKindSchema = external_exports.enum([
4531
+ "runbook_path",
4532
+ "github_run_url",
4533
+ "vercel_deployment_id",
4534
+ "neon_query_evidence",
4535
+ "report_hash",
4536
+ "external_assessor_ref"
4537
+ ]);
4538
+ var attestationEvidenceRefSchema = external_exports.object({
4539
+ kind: attestationEvidenceRefKindSchema,
4540
+ /** Redacted reference string (path, URL, ID, or hash). Max 512 chars. */
4541
+ ref: external_exports.string().min(1).max(512),
4542
+ /** Optional one-line redacted description. */
4543
+ note: external_exports.string().max(280).optional()
4544
+ });
4545
+ var createAttestationInputSchema = external_exports.object({
4546
+ studioId: external_exports.string().uuid(),
4547
+ complianceProfileId: external_exports.string().uuid(),
4548
+ productId: external_exports.string().uuid().nullable().optional(),
4549
+ framework: external_exports.string().min(1).max(120),
4550
+ kind: attestationKindSchema,
4551
+ outcome: attestationOutcomeSchema,
4552
+ /** Redacted operator statement; no payloads. */
4553
+ statement: external_exports.string().min(1).max(4e3),
4554
+ evidenceRefs: external_exports.array(attestationEvidenceRefSchema).min(1),
4555
+ supersedesAttestationId: external_exports.string().uuid().nullable().optional(),
4556
+ metadata: external_exports.record(external_exports.unknown()).default({})
4557
+ });
4558
+
4559
+ // ../shared/src/types/standup-provisioning.ts
4560
+ var ScaffoldFileSchema = external_exports.object({
4561
+ path: external_exports.string().min(1),
4562
+ contents: external_exports.string()
4563
+ }).strict();
4564
+ var ScaffoldManifestSchema = external_exports.array(ScaffoldFileSchema).min(1);
4565
+ var RepoRefSchema = external_exports.object({
4566
+ studioId: external_exports.string().uuid(),
4567
+ owner: external_exports.string().min(1),
4568
+ // GitHub org/account that owns the repo
4569
+ name: external_exports.string().min(1),
4570
+ fullName: external_exports.string().min(1),
4571
+ // owner/name
4572
+ htmlUrl: external_exports.string().url(),
4573
+ defaultBranch: external_exports.string().min(1).default("main")
4574
+ }).strict();
4575
+ var ScopedTokenSchema = external_exports.object({
4576
+ token: external_exports.string().min(1),
4577
+ expiresAt: external_exports.string(),
4578
+ // ISO-8601
4579
+ scope: external_exports.string().min(1)
4580
+ // human-readable, e.g. "contents:write on owner/name"
4581
+ }).strict();
4582
+ var BranchProtectionStatusSchema = external_exports.object({
4583
+ protected: external_exports.boolean(),
4584
+ requiresPullRequestReview: external_exports.boolean(),
4585
+ requiredApprovingReviewCount: external_exports.number().int().nonnegative(),
4586
+ allowsForcePushes: external_exports.boolean(),
4587
+ allowsDeletions: external_exports.boolean()
4588
+ }).strict();
4589
+ var RenderParamsSchema = external_exports.object({
4590
+ productName: external_exports.string().min(1),
4591
+ slug: external_exports.string().min(1),
4592
+ vertical: external_exports.string().min(1)
4593
+ }).strict();
4594
+ var TemplateInputFieldSchema = external_exports.object({
4595
+ name: external_exports.string().min(1),
4596
+ label: external_exports.string().min(1),
4597
+ inputType: external_exports.enum(["text", "number", "date", "slug"]),
4598
+ required: external_exports.boolean().default(true),
4599
+ helperText: external_exports.string().min(1),
4600
+ example: external_exports.string().min(1),
4601
+ defaultValue: external_exports.string().optional()
4602
+ }).strict();
4603
+ var TemplateInputSchema = external_exports.object({
4604
+ fields: external_exports.array(TemplateInputFieldSchema).min(1)
4605
+ }).strict();
4606
+ var TemplateDescriptorSchema = external_exports.object({
4607
+ templateRef: external_exports.string().min(1),
4608
+ vertical: external_exports.string().min(1),
4609
+ description: external_exports.string().default(""),
4610
+ inputSchema: TemplateInputSchema.optional()
4611
+ }).strict();
4612
+ var StandupDryRunPlanSchema = external_exports.object({
4613
+ planOnly: external_exports.literal(true),
4614
+ planOnlyMessage: external_exports.string().min(1),
4615
+ product: external_exports.object({
4616
+ studioId: external_exports.string().min(1),
4617
+ productId: external_exports.string().min(1),
4618
+ name: external_exports.string().min(1),
4619
+ slug: external_exports.string().min(1),
4620
+ vertical: external_exports.string().min(1)
4621
+ }).strict(),
4622
+ targetRepo: external_exports.object({
4623
+ owner: external_exports.string().min(1),
4624
+ name: external_exports.string().min(1),
4625
+ fullName: external_exports.string().min(1),
4626
+ defaultBranch: external_exports.string().min(1),
4627
+ plannedState: external_exports.literal("would_create"),
4628
+ exists: external_exports.literal(false)
4629
+ }).strict(),
4630
+ template: TemplateDescriptorSchema,
4631
+ scaffold: external_exports.object({
4632
+ label: external_exports.literal("scaffold (template output)"),
4633
+ files: ScaffoldManifestSchema
4634
+ }).strict(),
4635
+ buildAgentSeed: external_exports.object({
4636
+ label: external_exports.literal("build-agent instructions (not yet run)"),
4637
+ instructions: external_exports.array(external_exports.string().min(1)).min(1),
4638
+ product: external_exports.object({
4639
+ name: external_exports.string().min(1),
4640
+ slug: external_exports.string().min(1),
4641
+ vertical: external_exports.string().min(1)
4642
+ }).strict(),
4643
+ repoFullName: external_exports.string().min(1)
4644
+ }).strict(),
4645
+ safetyEnvelope: external_exports.object({
4646
+ estimatedCostUsd: external_exports.number().finite().nonnegative(),
4647
+ costCapUsd: external_exports.number().finite().nonnegative(),
4648
+ networkPolicy: external_exports.object({
4649
+ mode: external_exports.literal("restricted"),
4650
+ allowedHosts: external_exports.array(external_exports.string().min(1)).min(1)
4651
+ }).strict(),
4652
+ scopedToken: external_exports.object({
4653
+ tokenMinted: external_exports.literal(false),
4654
+ scope: external_exports.string().min(1),
4655
+ ttlSeconds: external_exports.number().int().positive()
4656
+ }).strict()
4657
+ }).strict()
4658
+ }).strict();
4659
+ var ConstructionWorkflowArtifactKindSchema = external_exports.enum([
4660
+ "profile",
4661
+ "milestone_schedule",
4662
+ "risk_register",
4663
+ "submittal_register",
4664
+ "long_lead_log",
4665
+ "rfi_log",
4666
+ "responsibility_matrix"
4667
+ ]);
4668
+ var IsoDateSchema = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/);
4669
+ var ConstructionWorkflowTemplateDescriptorSchema = external_exports.object({
4670
+ templateRef: external_exports.string().min(1),
4671
+ workflowKind: external_exports.literal("construction_project"),
4672
+ templateKind: external_exports.literal("workflow_documents"),
4673
+ name: external_exports.string().min(1),
4674
+ description: external_exports.string().default(""),
4675
+ artifacts: external_exports.array(ConstructionWorkflowArtifactKindSchema).min(1),
4676
+ inputSchema: TemplateInputSchema
4677
+ }).strict();
4678
+ var ConstructionProjectSeedSchema = external_exports.object({
4679
+ projectName: external_exports.string().min(1).max(120),
4680
+ areaSqFt: external_exports.number().int().positive(),
4681
+ projectType: external_exports.string().min(1).max(120),
4682
+ buildingName: external_exports.string().min(1).max(160),
4683
+ ntpDate: IsoDateSchema,
4684
+ scheduleTargetDate: IsoDateSchema
4685
+ }).strict();
4686
+ var ConstructionMilestoneSchema = external_exports.object({
4687
+ id: external_exports.string().min(1),
4688
+ name: external_exports.string().min(1),
4689
+ targetDate: IsoDateSchema,
4690
+ status: external_exports.enum(["planned", "at_risk", "slipped", "complete"])
4691
+ }).strict();
4692
+ var ConstructionSubmittalSchema = external_exports.object({
4693
+ id: external_exports.string().min(1),
4694
+ item: external_exports.string().min(1),
4695
+ division: external_exports.string().min(1),
4696
+ needByDate: IsoDateSchema,
4697
+ status: external_exports.enum(["not_started", "open", "in_review", "approved", "done"])
4698
+ }).strict();
4699
+ var ConstructionRfiSchema = external_exports.object({
4700
+ id: external_exports.string().min(1),
4701
+ subject: external_exports.string().min(1),
4702
+ to: external_exports.string().min(1),
4703
+ status: external_exports.enum(["draft", "open", "answered", "closed"]),
4704
+ openedDate: IsoDateSchema
4705
+ }).strict();
4706
+ var ConstructionRiskSchema = external_exports.object({
4707
+ id: external_exports.string().min(1),
4708
+ risk: external_exports.string().min(1),
4709
+ rating: external_exports.enum(["low", "medium", "high"]),
4710
+ mitigation: external_exports.string().min(1),
4711
+ confidence: external_exports.number().min(0).max(1)
4712
+ }).strict();
4713
+ var ConstructionLongLeadItemSchema = external_exports.object({
4714
+ id: external_exports.string().min(1),
4715
+ item: external_exports.string().min(1),
4716
+ needByDate: IsoDateSchema,
4717
+ status: external_exports.enum(["tracking", "ordered", "received", "done"]),
4718
+ assumption: external_exports.string().min(1)
4719
+ }).strict();
4720
+ var ConstructionResponsibilitySchema = external_exports.object({
4721
+ id: external_exports.string().min(1),
4722
+ role: external_exports.string().min(1),
4723
+ owner: external_exports.string().min(1),
4724
+ responsibility: external_exports.string().min(1)
4725
+ }).strict();
4726
+ var ConstructionMarkdownArtifactSchema = external_exports.object({
4727
+ kind: ConstructionWorkflowArtifactKindSchema,
4728
+ path: external_exports.string().min(1),
4729
+ markdown: external_exports.string().min(1)
4730
+ }).strict();
4731
+ var ConstructionWorkflowArtifactSetSchema = external_exports.object({
4732
+ projectName: external_exports.string().min(1),
4733
+ generatedAt: external_exports.string().datetime(),
4734
+ assumptions: external_exports.array(external_exports.string().min(1)).default([]),
4735
+ needsPmData: external_exports.array(external_exports.string().min(1)).default([]),
4736
+ markdownArtifacts: external_exports.array(ConstructionMarkdownArtifactSchema).min(1),
4737
+ structured: external_exports.object({
4738
+ milestones: external_exports.array(ConstructionMilestoneSchema),
4739
+ submittals: external_exports.array(ConstructionSubmittalSchema),
4740
+ rfis: external_exports.array(ConstructionRfiSchema),
4741
+ risks: external_exports.array(ConstructionRiskSchema),
4742
+ longLeadItems: external_exports.array(ConstructionLongLeadItemSchema),
4743
+ responsibilities: external_exports.array(ConstructionResponsibilitySchema)
4744
+ }).strict()
4745
+ }).strict();
4746
+ var ConstructionWorkflowDryRunPlanSchema = external_exports.object({
4747
+ planOnly: external_exports.literal(true),
4748
+ planOnlyMessage: external_exports.string().min(1),
4749
+ product: external_exports.object({
4750
+ studioId: external_exports.string().min(1),
4751
+ productId: external_exports.string().min(1),
4752
+ name: external_exports.string().min(1),
4753
+ vertical: external_exports.string().min(1)
4754
+ }).strict(),
4755
+ template: ConstructionWorkflowTemplateDescriptorSchema,
4756
+ seed: ConstructionProjectSeedSchema,
4757
+ safetyEnvelope: external_exports.object({
4758
+ executor: external_exports.literal("benign_workflow_populator"),
4759
+ sandboxUsed: external_exports.literal(false),
4760
+ scopedTokenMinted: external_exports.literal(false),
4761
+ externalWritesAllowed: external_exports.literal(false),
4762
+ workspaceWriteMode: external_exports.literal("markdown_only")
4763
+ }).strict()
4764
+ }).strict();
4765
+
4766
+ // ../shared/src/types/construction-log-excel.ts
4767
+ var CONSTRUCTION_LOG_EXCEL_SCHEMA_VERSION = "construction-log-excel-v1";
4768
+ var ConstructionLogExcelSheetKeySchema = external_exports.enum([
4769
+ "submittals",
4770
+ "rfis",
4771
+ "riskRegister",
4772
+ "longLead",
4773
+ "responsibilityMatrix",
4774
+ "milestones"
4775
+ ]);
4776
+ var ConstructionLogExcelColumnSchema = external_exports.object({
4777
+ key: external_exports.string().min(1),
4778
+ header: external_exports.string().min(1),
4779
+ required: external_exports.boolean(),
4780
+ description: external_exports.string().min(1)
4781
+ }).strict().describe("Canonical column definition for Alfred construction log Excel round-trips.");
4782
+ var ConstructionLogExcelSheetSchema = external_exports.object({
4783
+ key: ConstructionLogExcelSheetKeySchema,
4784
+ sheetName: external_exports.string().min(1),
4785
+ description: external_exports.string().min(1),
4786
+ columns: external_exports.array(ConstructionLogExcelColumnSchema).min(1)
4787
+ }).strict().describe("Canonical worksheet definition for Alfred construction log Excel round-trips.");
4788
+ var ConstructionLogExcelWorkbookSchema = external_exports.object({
4789
+ schemaVersion: external_exports.literal(CONSTRUCTION_LOG_EXCEL_SCHEMA_VERSION),
4790
+ sheets: external_exports.record(ConstructionLogExcelSheetKeySchema, ConstructionLogExcelSheetSchema)
4791
+ }).strict().describe(
4792
+ "Canonical Alfred construction log Excel workbook schema shared by native output generation and export-upload ingestion."
4793
+ );
4794
+ var CONSTRUCTION_LOG_EXCEL_SHEETS = {
4795
+ submittals: {
4796
+ key: "submittals",
4797
+ sheetName: "Submittals",
4798
+ description: "Submittal register rows generated by Alfred or edited by the PM.",
4799
+ columns: [
4800
+ column("id", "ID", true, "Stable submittal identifier, for example SUB-001."),
4801
+ column("item", "Item", true, "Submittal item or package name."),
4802
+ column("division", "Division", true, "Spec division or trade grouping."),
4803
+ column("needByDate", "Need By Date", true, "Date needed in YYYY-MM-DD format."),
4804
+ column("status", "Status", true, "One of not_started, open, in_review, approved, or done."),
4805
+ commonColumn("assumptionsNotes")
4806
+ ]
4807
+ },
4808
+ rfis: {
4809
+ key: "rfis",
4810
+ sheetName: "RFIs",
4811
+ description: "RFI log rows generated by Alfred or edited by the PM.",
4812
+ columns: [
4813
+ column("id", "ID", true, "Stable RFI identifier, for example RFI-001."),
4814
+ column("subject", "Subject", true, "RFI subject."),
4815
+ column("to", "To", true, "Responsible recipient or ball-in-court party."),
4816
+ column("status", "Status", true, "One of draft, open, answered, or closed."),
4817
+ column("openedDate", "Opened Date", true, "Date opened in YYYY-MM-DD format."),
4818
+ commonColumn("assumptionsNotes")
4819
+ ]
4820
+ },
4821
+ riskRegister: {
4822
+ key: "riskRegister",
4823
+ sheetName: "Risk Register",
4824
+ description: "Risk register rows generated by Alfred or edited by the PM.",
4825
+ columns: [
4826
+ column("id", "ID", true, "Stable risk identifier, for example RISK-001."),
4827
+ column("risk", "Risk", true, "Risk statement."),
4828
+ column("rating", "Rating", true, "One of low, medium, or high."),
4829
+ column("mitigation", "Mitigation", true, "Mitigation plan."),
4830
+ column("confidence", "Confidence", true, "Confidence score from 0 to 1."),
4831
+ commonColumn("assumptionsNotes")
4832
+ ]
4833
+ },
4834
+ longLead: {
4835
+ key: "longLead",
4836
+ sheetName: "Long-Lead",
4837
+ description: "Long-lead procurement rows generated by Alfred or edited by the PM.",
4838
+ columns: [
4839
+ column("id", "ID", true, "Stable long-lead identifier, for example LL-001."),
4840
+ column("item", "Item", true, "Long-lead item name."),
4841
+ column("needByDate", "Need By Date", true, "Date needed in YYYY-MM-DD format."),
4842
+ column("status", "Status", true, "One of tracking, ordered, received, or done."),
4843
+ column("assumption", "Assumption", true, "Procurement assumption behind this row."),
4844
+ commonColumn("assumptionsNotes")
4845
+ ]
4846
+ },
4847
+ responsibilityMatrix: {
4848
+ key: "responsibilityMatrix",
4849
+ sheetName: "Responsibility Matrix",
4850
+ description: "Responsibility matrix rows generated by Alfred or edited by the PM.",
4851
+ columns: [
4852
+ column("id", "ID", true, "Stable responsibility identifier, for example RESP-001."),
4853
+ column("role", "Role", true, "Role or party."),
4854
+ column("owner", "Owner", true, "Named owner, or Needs PM input when unknown."),
4855
+ column("responsibility", "Responsibility", true, "Responsibility statement."),
4856
+ commonColumn("assumptionsNotes")
4857
+ ]
4858
+ },
4859
+ milestones: {
4860
+ key: "milestones",
4861
+ sheetName: "Milestones",
4862
+ description: "Milestone schedule rows used to generate the MS Project XML schedule.",
4863
+ columns: [
4864
+ column("id", "ID", true, "Stable milestone identifier, for example MS-001."),
4865
+ column("name", "Name", true, "Milestone or task name."),
4866
+ column("targetDate", "Target Date", true, "Target date in YYYY-MM-DD format."),
4867
+ column("status", "Status", true, "One of planned, at_risk, slipped, or complete."),
4868
+ column("predecessorId", "Predecessor ID", false, "Optional predecessor milestone ID."),
4869
+ commonColumn("assumptionsNotes")
4870
+ ]
4871
+ }
4872
+ };
4873
+ var CONSTRUCTION_LOG_EXCEL_WORKBOOK_SCHEMA = ConstructionLogExcelWorkbookSchema.parse({
4874
+ schemaVersion: CONSTRUCTION_LOG_EXCEL_SCHEMA_VERSION,
4875
+ sheets: CONSTRUCTION_LOG_EXCEL_SHEETS
4876
+ });
4877
+ function column(key, header, required, description) {
4878
+ return { key, header, required, description };
4879
+ }
4880
+ function commonColumn(key) {
4881
+ return column(
4882
+ key,
4883
+ "Assumptions / Notes",
4884
+ false,
4885
+ "Show-your-work note, including generated assumptions and PM-input gaps."
4886
+ );
4887
+ }
4888
+
4889
+ // ../shared/src/types/dev-task-dispatch.ts
4890
+ var CodingAgentExecutorSchema = external_exports.enum(["claude-code", "codex"]);
4891
+ var DevTaskDispatchRequestSchema = external_exports.object({
4892
+ studioId: external_exports.string().uuid(),
4893
+ productId: external_exports.string().uuid(),
4894
+ repo: RepoRefSchema,
4895
+ task: external_exports.string().min(1).max(2e4),
4896
+ executor: CodingAgentExecutorSchema
4897
+ }).strict();
4898
+ var DevTaskDispatchDryRunPlanSchema = external_exports.object({
4899
+ planOnly: external_exports.literal(true),
4900
+ planOnlyMessage: external_exports.string().min(1),
4901
+ targetRepo: RepoRefSchema,
4902
+ task: external_exports.string().min(1),
4903
+ executor: CodingAgentExecutorSchema,
4904
+ branchName: external_exports.string().min(1),
4905
+ safetyEnvelope: external_exports.object({
4906
+ estimatedCostUsd: external_exports.number().finite().nonnegative(),
4907
+ costCapUsd: external_exports.number().finite().nonnegative(),
4908
+ timeoutMs: external_exports.number().int().positive(),
4909
+ scopedToken: external_exports.object({
4910
+ tokenMinted: external_exports.literal(false),
4911
+ scope: external_exports.string().min(1),
4912
+ ttlSeconds: external_exports.number().int().positive()
4913
+ }).strict(),
4914
+ prOnly: external_exports.object({
4915
+ opensPullRequest: external_exports.literal(true),
4916
+ mergesPullRequest: external_exports.literal(false),
4917
+ pushesMain: external_exports.literal(false),
4918
+ deploys: external_exports.literal(false)
4919
+ }).strict()
4920
+ }).strict()
4921
+ }).strict();
4922
+ var CodingAgentRunInputSchema = external_exports.object({
4923
+ executor: CodingAgentExecutorSchema,
4924
+ repo: RepoRefSchema,
4925
+ task: external_exports.string().min(1),
4926
+ scopedToken: ScopedTokenSchema,
4927
+ branchName: external_exports.string().min(1),
4928
+ costCapUsd: external_exports.number().finite().nonnegative(),
4929
+ timeoutMs: external_exports.number().int().positive()
4930
+ }).strict();
4931
+ var CodingAgentRunResultSchema = external_exports.discriminatedUnion("status", [
4932
+ external_exports.object({
4933
+ status: external_exports.literal("succeeded"),
4934
+ prUrl: external_exports.string().url(),
4935
+ summary: external_exports.string().min(1),
4936
+ costUsd: external_exports.number().finite().nonnegative(),
4937
+ /**
4938
+ * The exact commit sha the PR branch was pushed at when it opened —
4939
+ * captured server-side (see server-dispatch-backend.ts) right after a
4940
+ * successful run, before the workspace is torn down. Lets the Maeve
4941
+ * in-flight-fix detector (maeve-in-flight-pr-fix-lesson.ts) key off the
4942
+ * real PR-open head instead of falling back to timestamp matching.
4943
+ */
4944
+ openedHeadSha: external_exports.string().min(1).optional()
4945
+ }).strict(),
4946
+ external_exports.object({
4947
+ status: external_exports.literal("failed"),
4948
+ summary: external_exports.string().min(1),
4949
+ reason: external_exports.string().min(1),
4950
+ costUsd: external_exports.number().finite().nonnegative().optional()
4951
+ }).strict()
4952
+ ]);
4953
+ var DevTaskOutcomeSchema = external_exports.enum(["ci_passed", "pr_merged", "ci_failed"]);
4954
+ var SecurityFindingClassSchema = external_exports.enum(["authz", "csrf", "injection", "secrets", "ssrf"]);
4955
+ var RecordDevTaskOutcomeInputSchema = external_exports.object({
4956
+ product: external_exports.string().min(1).max(120),
4957
+ repo: external_exports.string().min(1).max(240),
4958
+ prUrl: external_exports.string().url(),
4959
+ outcome: DevTaskOutcomeSchema,
4960
+ task: external_exports.string().min(1).max(2e4).optional(),
4961
+ executor: CodingAgentExecutorSchema.optional(),
4962
+ occurredAt: external_exports.string().datetime().optional(),
4963
+ maeveLessonStatement: external_exports.string().min(1).max(1e3).optional(),
4964
+ maeveLessonTags: external_exports.array(external_exports.string().min(1).max(120)).optional(),
4965
+ /** Security metadata — enriches the Maeve lesson with class/pattern, never raw vulnerable detail. */
4966
+ findingClass: SecurityFindingClassSchema.optional(),
4967
+ ruleId: external_exports.string().min(1).max(120).optional(),
4968
+ signalSource: external_exports.string().min(1).max(240).optional()
4969
+ }).strict();
4970
+
4971
+ // ../shared/src/schemas/standup-plan.ts
4972
+ var StandupRequestSchema = external_exports.object({
4973
+ productName: external_exports.string().min(1).max(80),
4974
+ slug: external_exports.string().min(1).optional(),
4975
+ vertical: external_exports.string().min(1).max(80),
4976
+ repoTarget: external_exports.string().min(1).default("barg-labs"),
4977
+ repoName: external_exports.string().min(1).optional(),
4978
+ templateRef: external_exports.string().min(1).optional(),
4979
+ scopeNotes: external_exports.string().max(2e3).optional()
4980
+ }).strict();
4981
+ var StandupPlanSchema = external_exports.object({
4982
+ productName: external_exports.string().min(1).max(80),
4983
+ slug: external_exports.string().regex(/^[a-z0-9][a-z0-9-]*[a-z0-9]$/),
4984
+ vertical: external_exports.string().min(1).max(80),
4985
+ templateRef: external_exports.string().min(1),
4986
+ repoTarget: external_exports.string().min(1),
4987
+ repoName: external_exports.string().regex(/^[a-z0-9][a-z0-9-]*[a-z0-9]$/),
4988
+ fileManifest: external_exports.array(external_exports.string().min(1)).min(1),
4989
+ credentialScope: external_exports.string().min(1),
4990
+ estimatedCostUsd: external_exports.number().nonnegative(),
4991
+ steps: external_exports.array(external_exports.string().min(1)).min(1),
4992
+ dryRun: external_exports.literal(true)
4993
+ }).strict();
4994
+
4995
+ // ../shared/src/types/coordinator-prompt.ts
4996
+ var CoordinatorPromptContextBlockSchema = external_exports.object({
4997
+ title: external_exports.string().min(1).max(120),
4998
+ body: external_exports.string().min(1).max(6e3),
4999
+ source: external_exports.string().max(500).optional()
5000
+ });
5001
+ var CoordinatorPromptInputPayloadSchema = external_exports.object({
5002
+ objective: external_exports.string().min(1).max(4e3),
5003
+ productId: external_exports.string().min(1),
5004
+ context: external_exports.array(CoordinatorPromptContextBlockSchema).min(1).max(20),
5005
+ escalationHints: external_exports.array(external_exports.string().min(1).max(500)).max(10).optional()
5006
+ });
5007
+ var CoordinatorPromptTokensUsedSchema = external_exports.object({
5008
+ input: external_exports.number().int().nonnegative(),
5009
+ output: external_exports.number().int().nonnegative(),
5010
+ total: external_exports.number().int().nonnegative(),
5011
+ costCents: external_exports.number().nonnegative()
5012
+ });
5013
+ var CoordinatorPromptSchema = external_exports.object({
5014
+ id: external_exports.string().uuid(),
5015
+ studioId: external_exports.string().min(1),
5016
+ productId: external_exports.string().min(1),
5017
+ draftedAt: external_exports.coerce.date(),
5018
+ inputPayload: CoordinatorPromptInputPayloadSchema,
5019
+ markdown: external_exports.string().min(1),
5020
+ metadata: external_exports.object({
5021
+ tokensUsed: CoordinatorPromptTokensUsedSchema.optional(),
5022
+ model: external_exports.string().optional(),
5023
+ templateVersion: external_exports.string().optional()
5024
+ }).catchall(external_exports.unknown()).optional()
5025
+ });
5026
+ var CreateCoordinatorPromptInputSchema = external_exports.object({
5027
+ productId: external_exports.string().min(1),
5028
+ inputPayload: CoordinatorPromptInputPayloadSchema,
5029
+ markdown: external_exports.string().min(1),
5030
+ metadata: external_exports.record(external_exports.unknown()).optional(),
5031
+ draftedAt: external_exports.coerce.date().optional()
5032
+ });
5033
+ var ListCoordinatorPromptsInputSchema = external_exports.object({
5034
+ productId: external_exports.string().min(1).optional(),
5035
+ since: external_exports.coerce.date().optional()
5036
+ });
5037
+
5038
+ // ../shared/src/schemas/inbox-triage.ts
5039
+ var InboxTriageClassificationSchema = external_exports.enum([
5040
+ "needs_reply",
5041
+ "fyi",
5042
+ "waiting",
5043
+ "escalate"
5044
+ ]);
5045
+ var InboxTriageLegacyUrgencySchema = external_exports.enum(["needs_reply", "fyi", "noise", "escalation"]);
5046
+ var InboxTriageThreadSchema = external_exports.object({
5047
+ id: external_exports.string().min(1),
5048
+ subject: external_exports.string().min(1),
5049
+ from: external_exports.string(),
5050
+ to: external_exports.string(),
5051
+ snippet: external_exports.string(),
5052
+ labels: external_exports.array(external_exports.string()),
5053
+ unread: external_exports.boolean(),
5054
+ needsReply: external_exports.boolean(),
5055
+ updatedAt: external_exports.string().min(1),
5056
+ url: external_exports.string().min(1),
5057
+ messageCount: external_exports.number().int().nonnegative()
5058
+ });
5059
+ var InboxTriageResultSchema = external_exports.object({
5060
+ threadId: external_exports.string().min(1),
5061
+ classification: InboxTriageClassificationSchema,
5062
+ confidence: external_exports.number().min(0).max(1),
5063
+ reason: external_exports.string(),
5064
+ labelName: external_exports.string(),
5065
+ applied: external_exports.boolean(),
5066
+ error: external_exports.string().optional()
5067
+ });
5068
+ var TriageInboxUsageSchema = external_exports.object({
5069
+ input_tokens: external_exports.number().int().nonnegative(),
5070
+ output_tokens: external_exports.number().int().nonnegative(),
5071
+ cached_tokens: external_exports.number().int().nonnegative().optional(),
5072
+ cost_cents: external_exports.number().nonnegative()
5073
+ });
5074
+ var TriageInboxResultSchema = external_exports.object({
5075
+ productId: external_exports.string().min(1),
5076
+ results: external_exports.array(InboxTriageResultSchema),
5077
+ usage: TriageInboxUsageSchema.optional()
5078
+ });
5079
+
5080
+ // ../shared/src/schemas/inference-usage.ts
5081
+ var InferenceUsageSurfaceSchema = external_exports.string().min(1).max(80).regex(/^[a-z][a-z0-9_-]*$/);
5082
+ var RecordInferenceUsageInputSchema = external_exports.object({
5083
+ product: external_exports.string().min(1).optional(),
5084
+ surface: InferenceUsageSurfaceSchema,
5085
+ model: external_exports.string().min(1).max(120),
5086
+ inputTokens: external_exports.number().int().nonnegative(),
5087
+ outputTokens: external_exports.number().int().nonnegative(),
5088
+ cachedTokens: external_exports.number().int().nonnegative().default(0),
5089
+ costCents: external_exports.number().nonnegative(),
5090
+ timestamp: external_exports.coerce.date().optional()
5091
+ });
5092
+ var InferenceUsageBreakdownQuerySchema = external_exports.object({
5093
+ from: external_exports.coerce.date(),
5094
+ to: external_exports.coerce.date()
5095
+ });
5096
+ var InferenceUsageGroupSchema = external_exports.object({
5097
+ calls: external_exports.number().int().nonnegative(),
5098
+ costCents: external_exports.number().nonnegative(),
5099
+ inputTokens: external_exports.number().int().nonnegative(),
5100
+ outputTokens: external_exports.number().int().nonnegative(),
5101
+ cachedTokens: external_exports.number().int().nonnegative()
5102
+ });
5103
+ var InferenceUsageBreakdownSchema = external_exports.object({
5104
+ from: external_exports.string().datetime(),
5105
+ to: external_exports.string().datetime(),
5106
+ totals: InferenceUsageGroupSchema,
5107
+ bySurface: external_exports.array(InferenceUsageGroupSchema.extend({ surface: InferenceUsageSurfaceSchema })),
5108
+ byModel: external_exports.array(InferenceUsageGroupSchema.extend({ model: external_exports.string().min(1) }))
5109
+ });
5110
+
5111
+ // ../shared/src/schemas/product-health.ts
5112
+ var DEFAULT_PRODUCT_HEALTH_INTERVAL_SECONDS = 24 * 60 * 60;
5113
+ var ISO_DATE_STRING = external_exports.string().datetime({ offset: true }).describe("ISO-8601 timestamp with timezone offset.");
5114
+ var ProductHealthStatusSchema = external_exports.enum(["healthy", "degraded", "halted"]);
5115
+ var ProductHealthDetailsSchema = external_exports.record(external_exports.unknown()).superRefine((details, ctx) => {
5116
+ const credentialPath = findCredentialLikePath(details);
5117
+ if (!credentialPath) return;
5118
+ ctx.addIssue({
5119
+ code: external_exports.ZodIssueCode.custom,
5120
+ message: `Product health details must not contain credentials (${credentialPath}).`
5121
+ });
5122
+ });
5123
+ var ProductHealthReportInputSchema = external_exports.object({
5124
+ productId: external_exports.string().min(1).max(120),
5125
+ status: ProductHealthStatusSchema,
5126
+ lastActivityAt: ISO_DATE_STRING,
5127
+ summary: external_exports.string().min(1).max(1e3),
5128
+ details: ProductHealthDetailsSchema.optional(),
5129
+ reportedAt: ISO_DATE_STRING,
5130
+ expectedIntervalSeconds: external_exports.number().int().positive().optional()
5131
+ });
5132
+ var StoredProductHealthReportSchema = ProductHealthReportInputSchema.extend({
5133
+ expectedIntervalSeconds: external_exports.number().int().positive()
5134
+ });
5135
+ var ProductLivenessStateSchema = external_exports.enum([
5136
+ "healthy",
5137
+ "degraded",
5138
+ "halted",
5139
+ "stale",
5140
+ "unknown"
5141
+ ]);
5142
+ var ProductLivenessSnapshotEntrySchema = external_exports.object({
5143
+ productId: external_exports.string().min(1),
5144
+ state: ProductLivenessStateSchema,
5145
+ flagged: external_exports.boolean(),
5146
+ status: ProductHealthStatusSchema.optional(),
5147
+ lastActivityAt: ISO_DATE_STRING.optional(),
5148
+ reportedAt: ISO_DATE_STRING.optional(),
5149
+ expectedIntervalSeconds: external_exports.number().int().positive(),
5150
+ ageSeconds: external_exports.number().int().nonnegative().nullable(),
5151
+ summary: external_exports.string()
5152
+ });
5153
+ function findCredentialLikePath(value, path = []) {
5154
+ if (!value || typeof value !== "object") return null;
5155
+ if (Array.isArray(value)) {
5156
+ for (const [index, item] of value.entries()) {
5157
+ const match = findCredentialLikePath(item, [...path, String(index)]);
5158
+ if (match) return match;
5159
+ }
5160
+ return null;
5161
+ }
5162
+ for (const [key, nested] of Object.entries(value)) {
5163
+ const nextPath = [...path, key];
5164
+ if (/(?:secret|token|password|credential|api[-_]?key|private[-_]?key)/i.test(key)) {
5165
+ return nextPath.join(".");
5166
+ }
5167
+ const match = findCredentialLikePath(nested, nextPath);
5168
+ if (match) return match;
5169
+ }
5170
+ return null;
5171
+ }
5172
+
5173
+ // ../shared/src/schemas/interaction-trace.ts
5174
+ var InteractionTraceVerificationSignalSchema = external_exports.enum([
5175
+ "no_signal",
5176
+ "human_accepted_output",
5177
+ "human_rejected_output",
5178
+ "ci_pr_succeeded",
5179
+ "ci_pr_failed",
5180
+ "brief_accepted",
5181
+ "brief_rejected",
5182
+ "standup_completed",
5183
+ "standup_failed"
5184
+ ]);
5185
+ var InteractionTraceVerificationSchema = external_exports.object({
5186
+ success: external_exports.boolean(),
5187
+ signal: InteractionTraceVerificationSignalSchema,
5188
+ confidence: external_exports.number().min(0).max(1),
5189
+ reason: external_exports.string().min(1).max(1e3)
5190
+ });
5191
+ var InteractionTraceActorSchema = external_exports.object({
5192
+ type: external_exports.enum(["human", "agent", "system"]),
5193
+ id: external_exports.string().min(1).max(200),
5194
+ name: external_exports.string().min(1).max(200).optional()
5195
+ });
5196
+ var RecordInteractionTraceInputSchema = external_exports.object({
5197
+ product: external_exports.string().min(1).max(120),
5198
+ intent: external_exports.string().min(1).max(500),
5199
+ actionSummary: external_exports.string().min(1).max(4e3),
5200
+ outcomeSummary: external_exports.string().min(1).max(4e3),
5201
+ model: external_exports.string().min(1).max(200).nullable().optional(),
5202
+ actor: InteractionTraceActorSchema,
5203
+ occurredAt: external_exports.string().datetime().optional(),
5204
+ verification: InteractionTraceVerificationSchema.optional(),
5205
+ metadata: external_exports.record(external_exports.unknown()).optional()
5206
+ });
5207
+ var StoredInteractionTraceSchema = RecordInteractionTraceInputSchema.extend({
5208
+ id: external_exports.string().uuid(),
5209
+ occurredAt: external_exports.string().datetime(),
5210
+ createdAt: external_exports.string().datetime(),
5211
+ verification: InteractionTraceVerificationSchema
5212
+ });
5213
+ var InteractionTraceQualityScoreSchema = external_exports.number().min(0).max(1);
5214
+ var RetrieveRelevantTracesQuerySchema = external_exports.object({
5215
+ product: external_exports.string().min(1).max(120),
5216
+ intent: external_exports.string().min(1).max(500),
5217
+ limit: external_exports.number().int().positive().max(10).optional()
5218
+ });
5219
+ var RetrievedInteractionTraceSchema = StoredInteractionTraceSchema.extend({
5220
+ score: external_exports.number().min(0).max(1),
5221
+ qualityScore: InteractionTraceQualityScoreSchema
5222
+ });
5223
+
5224
+ // ../shared/src/schemas/maeve-lesson.ts
5225
+ var MaeveLessonStatusSchema = external_exports.enum(["candidate", "verified", "retired"]);
5226
+ var MaeveLessonEvidenceSchema = external_exports.object({
5227
+ source: external_exports.enum(["interaction_trace", "artifact"]),
5228
+ traceId: external_exports.string().uuid().optional(),
5229
+ artifact: external_exports.string().min(1).max(1e3).optional(),
5230
+ summary: external_exports.string().min(1).max(1e3),
5231
+ hash: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/)
5232
+ });
5233
+ var MaeveLessonSchema = external_exports.object({
5234
+ id: external_exports.string().uuid(),
5235
+ product: external_exports.string().min(1).max(120),
5236
+ statement: external_exports.string().min(1).max(1e3),
5237
+ scope: external_exports.array(external_exports.string().min(1).max(120)).default([]),
5238
+ tags: external_exports.array(external_exports.string().min(1).max(120)).default([]),
5239
+ evidence: external_exports.array(MaeveLessonEvidenceSchema),
5240
+ status: MaeveLessonStatusSchema,
5241
+ createdAt: external_exports.string().datetime(),
5242
+ lastSeenAt: external_exports.string().datetime()
5243
+ });
5244
+ var CreateMaeveLessonInputSchema = external_exports.object({
5245
+ product: external_exports.string().min(1).max(120),
5246
+ statement: external_exports.string().min(1).max(1e3),
5247
+ scope: external_exports.array(external_exports.string().min(1).max(120)).default([]),
5248
+ tags: external_exports.array(external_exports.string().min(1).max(120)).default([]),
5249
+ evidence: external_exports.array(MaeveLessonEvidenceSchema).default([]),
5250
+ status: MaeveLessonStatusSchema.default("candidate"),
5251
+ lastSeenAt: external_exports.string().datetime().optional()
5252
+ });
5253
+ var PromoteMaeveLessonInputSchema = external_exports.object({
5254
+ id: external_exports.string().uuid(),
5255
+ status: MaeveLessonStatusSchema,
5256
+ appendEvidence: external_exports.array(MaeveLessonEvidenceSchema).optional()
5257
+ });
5258
+ var RecallMaeveLessonsQuerySchema = external_exports.object({
5259
+ product: external_exports.string().min(1).max(120).optional(),
5260
+ taskType: external_exports.string().min(1).max(120).optional(),
5261
+ tags: external_exports.array(external_exports.string().min(1).max(120)).default([]),
5262
+ limit: external_exports.number().int().positive().max(10).default(5),
5263
+ /**
5264
+ * Free-text query for semantic (pgvector cosine) recall. When set and an
5265
+ * EmbeddingProvider is configured, recallMaeveLessons embeds this text and
5266
+ * retrieves by cosine similarity, with exact-tag matches as a boost.
5267
+ * Falls back to exact-class matching when the provider is NoOp.
5268
+ */
5269
+ semanticQuery: external_exports.string().min(1).max(500).optional()
5270
+ });
5271
+ var MaeveLessonSeedAnchorSchema = external_exports.object({
5272
+ commit: external_exports.string().regex(/^[0-9a-f]{40}$/i, "commit must be a full 40-character git SHA"),
5273
+ pr: external_exports.number().int().positive().nullable(),
5274
+ summary: external_exports.string().min(1).max(1e3)
5275
+ });
5276
+ var MaeveLessonSeedSchema = external_exports.object({
5277
+ statement: external_exports.string().min(1).max(1e3),
5278
+ scope: external_exports.array(external_exports.string().min(1).max(120)).default([]),
5279
+ tags: external_exports.array(external_exports.string().min(1).max(120)).default([]),
5280
+ anchors: external_exports.array(MaeveLessonSeedAnchorSchema).min(1),
5281
+ lastSeenAt: external_exports.string().datetime()
5282
+ });
5283
+ var ReviewFindingSeveritySchema = external_exports.enum(["critical", "high", "medium", "low"]);
5284
+ var ReviewFindingLessonInputSchema = external_exports.object({
5285
+ studioId: external_exports.string().min(1),
5286
+ findingStudioId: external_exports.string().min(1),
5287
+ product: external_exports.string().min(1).max(120),
5288
+ repository: external_exports.string().min(1).max(240),
5289
+ pullRequestNumber: external_exports.number().int().positive(),
5290
+ findingText: external_exports.string().min(1).max(4e3),
5291
+ severity: ReviewFindingSeveritySchema,
5292
+ reviewer: external_exports.string().min(1).max(120),
5293
+ remediatingCommitSha: external_exports.string().min(4).max(64),
5294
+ /** The durable, reusable rule distilled from the finding — NOT the finding text verbatim. */
5295
+ lessonStatement: external_exports.string().min(1).max(1e3),
5296
+ scope: external_exports.array(external_exports.string().min(1).max(120)).default([]),
5297
+ tags: external_exports.array(external_exports.string().min(1).max(120)).default([]),
5298
+ occurredAt: external_exports.string().datetime()
5299
+ });
5300
+
5301
+ // ../shared/src/schemas/operator-trace.ts
5302
+ var TRACE_ACTORS = [
5303
+ "human",
5304
+ "cowork",
5305
+ "claude-code",
5306
+ "codex",
5307
+ "goal-runner",
5308
+ "ci"
5309
+ ];
5310
+ var TraceActorSchema = external_exports.enum(TRACE_ACTORS);
5311
+ var TRACE_SOURCES = [
5312
+ "shell",
5313
+ "cowork",
5314
+ "claude-code",
5315
+ "codex",
5316
+ "goal-runner",
5317
+ "github"
5318
+ ];
5319
+ var TraceSourceSchema = external_exports.enum(TRACE_SOURCES);
5320
+ var TraceProvenanceSchema = external_exports.object({
5321
+ source: TraceSourceSchema,
5322
+ /** Absolute path of the source file/dir, or the gh query for CI events. */
5323
+ path: external_exports.string().min(1).max(1e3),
5324
+ /** Line number, session id, or PR number locating the row inside the source. */
5325
+ locator: external_exports.string().min(1).max(200).optional()
5326
+ });
5327
+ var TraceRecordSchema = external_exports.object({
5328
+ t: external_exports.string().datetime(),
5329
+ actor: TraceActorSchema,
5330
+ action: external_exports.string().min(1).max(2e3),
5331
+ target: external_exports.string().max(1e3).default(""),
5332
+ outcome: external_exports.string().max(2e3).default(""),
5333
+ product: external_exports.string().min(1).max(120).optional(),
5334
+ provenance: TraceProvenanceSchema
5335
+ });
5336
+ var ShellRedactionSchema = external_exports.enum(["none", "secret", "unclassified"]);
5337
+ var ShellTraceEntrySchema = external_exports.object({
5338
+ ts: external_exports.string().datetime(),
5339
+ cwd: external_exports.string().min(1).max(1e3),
5340
+ command: external_exports.string().min(1).max(4e3),
5341
+ exitCode: external_exports.number().int(),
5342
+ shellSessionId: external_exports.string().min(1).max(200),
5343
+ redaction: ShellRedactionSchema
5344
+ });
5345
+ var TraceSourceReportSchema = external_exports.discriminatedUnion("status", [
5346
+ external_exports.object({
5347
+ status: external_exports.literal("ok"),
5348
+ source: TraceSourceSchema,
5349
+ recordCount: external_exports.number().int().nonnegative(),
5350
+ /** Files/entries present but skipped (unparseable, filtered) — named, not silent. */
5351
+ skipped: external_exports.array(external_exports.string().min(1).max(500)).default([])
5352
+ }),
5353
+ external_exports.object({
5354
+ status: external_exports.literal("dark"),
5355
+ source: TraceSourceSchema,
5356
+ reason: external_exports.string().min(1).max(500)
5357
+ })
5358
+ ]);
5359
+ var OperatorTimelineSchema = external_exports.object({
5360
+ records: external_exports.array(TraceRecordSchema),
5361
+ sources: external_exports.array(TraceSourceReportSchema)
5362
+ });
5363
+
5364
+ // ../shared/src/schemas/witan.ts
4515
5365
  var WITAN_RUBRIC_VERSION_V1 = "witan-rubric-v1-2026-06-24";
4516
5366
  var WITAN_RUBRIC_VERSION_V2 = "witan-rubric-v2-2026-07-12";
4517
- var WITAN_RUBRIC_VERSION = WITAN_RUBRIC_VERSION_V2;
5367
+ var WITAN_RUBRIC_VERSION_V3 = "witan-rubric-v3-2026-07-13";
5368
+ var WITAN_RUBRIC_VERSION = WITAN_RUBRIC_VERSION_V3;
4518
5369
  var WITAN_TRADING_RUBRIC_VERSION_V0 = "witan-trading-rubric-v0-2026-07-01";
4519
5370
  var WITAN_CRITERION_IDS = [
4520
5371
  "A1",
@@ -4540,9 +5391,11 @@ var WITAN_TRADING_RUBRIC_V0_CRITERION_IDS = [
4540
5391
  "audit-completeness",
4541
5392
  "claim-reality"
4542
5393
  ];
5394
+ var WITAN_QUANT_INTEGRITY_RUBRIC_V0_CRITERION_IDS = ["quant-integrity"];
4543
5395
  var WitanCriterionIdSchema = external_exports.enum([
4544
5396
  ...WITAN_CRITERION_IDS,
4545
- ...WITAN_TRADING_RUBRIC_V0_CRITERION_IDS
5397
+ ...WITAN_TRADING_RUBRIC_V0_CRITERION_IDS,
5398
+ ...WITAN_QUANT_INTEGRITY_RUBRIC_V0_CRITERION_IDS
4546
5399
  ]);
4547
5400
  var WitanTradingCriterionIdSchema = external_exports.enum(WITAN_TRADING_RUBRIC_V0_CRITERION_IDS);
4548
5401
  var WitanCriterionCategorySchema = external_exports.enum([
@@ -4584,6 +5437,7 @@ var WITAN_REPO_ARCHETYPES = [
4584
5437
  "monorepo",
4585
5438
  "docs_only",
4586
5439
  "binary_only",
5440
+ "unrecognised_ecosystem",
4587
5441
  "empty"
4588
5442
  ];
4589
5443
  var WitanRepoArchetypeSchema = external_exports.enum(WITAN_REPO_ARCHETYPES);
@@ -4697,72 +5551,1074 @@ var WitanReportSchema = external_exports.object({
4697
5551
  categoryScores: external_exports.record(external_exports.string(), external_exports.number().min(0).max(4)).optional(),
4698
5552
  // Repo-archetype metadata (see WitanRepoArchetypeSchema above). Both fields are omitted for
4699
5553
  // callers that don't classify an archetype (e.g. the trading rubric). insufficientSourceReason
4700
- // is present only for the non-source archetypes ('docs_only' | 'binary_only' | 'empty') — its
4701
- // presence is what presentation layers (badge/terminal/verdict) key off to show an explicit
4702
- // insufficient-source verdict instead of a confident numeric score.
5554
+ // is present only for the non-source archetypes ('docs_only' | 'binary_only' |
5555
+ // 'unrecognised_ecosystem' | 'empty') — its presence is what presentation layers
5556
+ // (badge/terminal/verdict) key off to show an explicit insufficient-source verdict instead of
5557
+ // a confident numeric score.
4703
5558
  archetype: WitanRepoArchetypeSchema.optional(),
4704
5559
  insufficientSourceReason: external_exports.string().min(1).max(2e3).optional()
4705
5560
  }).strict();
4706
5561
 
4707
- // src/witan/repo-signals.ts
4708
- import { execFileSync } from "child_process";
4709
- import { createHash } from "crypto";
4710
- import { readFileSync, readdirSync, realpathSync, statSync } from "fs";
4711
- import { basename, join, relative, resolve, sep } from "path";
5562
+ // ../shared/src/schemas/work-item.ts
5563
+ var WorkItemTypeSchema = external_exports.enum(["feature", "bug"]);
5564
+ var WorkItemPrioritySchema = external_exports.enum(["low", "normal", "high", "urgent"]);
5565
+ var WorkItemStatusSchema = external_exports.enum([
5566
+ "submitted",
5567
+ "scoped",
5568
+ "dispatched",
5569
+ "pr_open",
5570
+ "done",
5571
+ "declined"
5572
+ ]);
5573
+ var CreateWorkItemInputSchema = external_exports.object({
5574
+ productId: external_exports.string().uuid(),
5575
+ type: WorkItemTypeSchema,
5576
+ title: external_exports.string().min(3).max(180),
5577
+ description: external_exports.string().min(8).max(2e4),
5578
+ acceptanceCriteria: external_exports.string().max(2e4).optional(),
5579
+ priority: WorkItemPrioritySchema.default("normal")
5580
+ }).strict();
5581
+ var ScopeWorkItemInputSchema = external_exports.object({
5582
+ scopedTask: external_exports.string().min(8).max(2e4).optional()
5583
+ }).strict();
5584
+ var ListWorkItemsQuerySchema = external_exports.object({
5585
+ productId: external_exports.string().uuid().optional(),
5586
+ status: WorkItemStatusSchema.optional()
5587
+ }).strict();
5588
+ var DispatchWorkItemInputSchema = external_exports.object({
5589
+ executor: external_exports.enum(["claude-code", "codex"]).default("codex"),
5590
+ mode: external_exports.enum(["dry-run", "execute"]).default("dry-run")
5591
+ }).strict();
4712
5592
 
4713
- // src/witan/json-safe.ts
4714
- function stripBom(text) {
4715
- return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
4716
- }
5593
+ // ../shared/src/schemas/goal-frontmatter.ts
5594
+ var GoalStreamKindSchema = external_exports.enum(["code", "process"]);
5595
+ var GoalPrioritySchema = external_exports.enum(["p0", "p1", "p2"]);
5596
+ var GoalFrontmatterExtrasSchema = external_exports.object({
5597
+ stream: external_exports.string().min(1).nullable().default(null),
5598
+ order: external_exports.number().int().nullable().default(null),
5599
+ dependsOn: external_exports.array(external_exports.string().min(1)).default([]),
5600
+ priority: GoalPrioritySchema.nullable().default(null),
5601
+ kind: GoalStreamKindSchema.default("code")
5602
+ });
4717
5603
 
4718
- // src/witan/repo-signals.ts
4719
- function buildWitanInputFromRepo(options) {
4720
- assertRepoPathExists(options.repoPath);
4721
- const generatedAt = options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
4722
- const headSha = readGitHead(options.repoPath);
4723
- const repoFiles = listRepoFiles(options.repoPath);
4724
- const archetype = classifyRepoArchetype(repoFiles);
5604
+ // ../shared/src/schemas/stream.ts
5605
+ var StreamSchema = external_exports.object({
5606
+ id: external_exports.string().uuid(),
5607
+ studioId: external_exports.string().uuid(),
5608
+ name: external_exports.string().min(1).max(120),
5609
+ tag: external_exports.string().max(60).nullable(),
5610
+ kind: GoalStreamKindSchema,
5611
+ orderIndex: external_exports.number().int(),
5612
+ createdAt: external_exports.coerce.date(),
5613
+ updatedAt: external_exports.coerce.date()
5614
+ });
5615
+ var StreamItemSchema = external_exports.object({
5616
+ id: external_exports.string().uuid(),
5617
+ studioId: external_exports.string().uuid(),
5618
+ streamId: external_exports.string().uuid(),
5619
+ orderIndex: external_exports.number().int(),
5620
+ title: external_exports.string().min(1).max(200),
5621
+ /** docs/orchestration/goal_*.md filename (no extension) for code items; null for process items. */
5622
+ goalName: external_exports.string().min(1).nullable(),
5623
+ refLabel: external_exports.string().max(60).nullable(),
5624
+ /** Free-form governed-run prompt text (mirrors the prototype's per-item `run` string). Null = no run control shown. */
5625
+ runPrompt: external_exports.string().min(1).nullable(),
5626
+ createdAt: external_exports.coerce.date(),
5627
+ updatedAt: external_exports.coerce.date()
5628
+ });
5629
+ var StreamItemDisplayStatusSchema = external_exports.enum([
5630
+ "done",
5631
+ "in-review",
5632
+ "running",
5633
+ "queued",
5634
+ "blocked",
5635
+ "local-unknown"
5636
+ ]);
5637
+
5638
+ // ../shared/src/schemas/runner-dispatch.ts
5639
+ var RUNNER_DISPATCH_MODES = ["dry-run", "execute"];
5640
+ var RunnerDispatchModeSchema = external_exports.enum(RUNNER_DISPATCH_MODES);
5641
+ var RUNNER_DISPATCH_STATUSES = ["queued", "claimed", "running", "done", "failed"];
5642
+ var RunnerDispatchStatusSchema = external_exports.enum(RUNNER_DISPATCH_STATUSES);
5643
+ var GOAL_CARD_PATTERN = /^docs\/orchestration\/[A-Za-z0-9_./-]+\.md$/;
5644
+ function isAllowedGoalCardPath(goalCard) {
5645
+ if (!GOAL_CARD_PATTERN.test(goalCard)) return false;
5646
+ return goalCard.split("/").every((segment) => segment !== "." && segment !== "..");
5647
+ }
5648
+ var GoalCardPathSchema = external_exports.string().min(1).max(300).refine(isAllowedGoalCardPath, { message: "goal_card_invalid" });
5649
+ var RunnerDispatchRequestSchema = external_exports.object({
5650
+ goalCard: GoalCardPathSchema,
5651
+ stream: external_exports.string().min(1).max(120).optional(),
5652
+ mode: RunnerDispatchModeSchema
5653
+ });
5654
+
5655
+ // ../shared/src/schemas/witan-metrics-manifest.ts
5656
+ var WITAN_METRICS_MANIFEST_SCHEMA_VERSION = "witan-metrics-manifest-v0";
5657
+ var WitanMetricsManifestReturnsFrequencySchema = external_exports.enum([
5658
+ "daily",
5659
+ "weekly",
5660
+ "monthly",
5661
+ "annual"
5662
+ ]);
5663
+ var WitanMetricsManifestFoldSchema = external_exports.object({
5664
+ foldId: external_exports.string().min(1).max(80),
5665
+ inSampleSharpe: external_exports.number().optional(),
5666
+ outOfSampleSharpe: external_exports.number().optional()
5667
+ }).strict();
5668
+ var WitanMetricsManifestCscvTrialSchema = external_exports.object({
5669
+ trialId: external_exports.string().min(1).max(80),
5670
+ blockReturns: external_exports.array(external_exports.number()).min(2)
5671
+ }).strict();
5672
+ var WitanMetricsManifestCscvSchema = external_exports.object({
5673
+ // Must be even (split into IS/OOS halves) — enforced by the grader, not the schema.
5674
+ blocks: external_exports.number().int().min(4).max(16),
5675
+ trials: external_exports.array(WitanMetricsManifestCscvTrialSchema).min(2).max(200)
5676
+ }).strict();
5677
+ var WitanMetricsManifestCostModelSchema = external_exports.object({
5678
+ feeBps: external_exports.number().min(0).optional(),
5679
+ slippageBps: external_exports.number().min(0).optional(),
5680
+ borrowBps: external_exports.number().min(0).optional(),
5681
+ notes: external_exports.string().min(1).max(300).optional()
5682
+ }).strict();
5683
+ var WitanMetricsManifestParameterPointSchema = external_exports.object({
5684
+ // Human-readable label for this point (e.g. "72" or "72 bars") — evidence/display only.
5685
+ paramLabel: external_exports.string().min(1).max(80),
5686
+ // The point's position on its swept axis, used to find its nearest neighbors by distance.
5687
+ paramValue: external_exports.number(),
5688
+ outOfSampleSharpe: external_exports.number()
5689
+ }).strict();
5690
+ var WitanMetricsManifestParameterSweepSchema = external_exports.object({
5691
+ // Name of the swept parameter (e.g. "lookback_bars") — evidence/display only.
5692
+ paramName: external_exports.string().min(1).max(80),
5693
+ points: external_exports.array(WitanMetricsManifestParameterPointSchema).min(3).max(500)
5694
+ }).strict();
5695
+ var WitanMetricsManifestLabelSourceSchema = external_exports.enum([
5696
+ "real_forward_returns",
5697
+ "synthetic",
5698
+ "unknown"
5699
+ ]);
5700
+ var WitanMetricsManifestSchema = external_exports.object({
5701
+ schemaVersion: external_exports.literal(WITAN_METRICS_MANIFEST_SCHEMA_VERSION),
5702
+ strategyName: external_exports.string().min(1).max(200).optional(),
5703
+ // The headline reported Sharpe ratio the target repo is claiming.
5704
+ reportedSharpe: external_exports.number(),
5705
+ // How many trials/backtests were run in the search that produced reportedSharpe (the
5706
+ // "1-of-42" count) — required so the grader can correct for selection bias.
5707
+ trialCount: external_exports.number().int().positive(),
5708
+ // Track-record length in return observations (e.g. 252 for a year of daily returns) —
5709
+ // the `n` in the Probabilistic/Deflated Sharpe Ratio formula.
5710
+ trackRecordObservations: external_exports.number().int().positive(),
5711
+ returnsFrequency: WitanMetricsManifestReturnsFrequencySchema.default("daily"),
5712
+ // Skew/kurtosis of the returns distribution; default to the normal-distribution values
5713
+ // (0 skew, kurtosis 3) when the repo does not report them.
5714
+ skewness: external_exports.number().default(0),
5715
+ kurtosis: external_exports.number().min(0).default(3),
5716
+ // Whether reportedSharpe is net of trading costs (fees/slippage/borrow/roll).
5717
+ netOfCosts: external_exports.boolean(),
5718
+ costModel: WitanMetricsManifestCostModelSchema.optional(),
5719
+ // Per-fold in-sample vs out-of-sample breakdown, when the repo reports one.
5720
+ folds: external_exports.array(WitanMetricsManifestFoldSchema).default([]),
5721
+ // Per-trial, per-block performance matrix for the CSCV/PBO computation, when available.
5722
+ cscv: WitanMetricsManifestCscvSchema.optional(),
5723
+ // Parameter-sweep breakdown for the run-tier (L3b) parameter-neighborhood robustness
5724
+ // grader, when the run swept a parameter (e.g. a lookback window) and recorded OOS
5725
+ // performance at each swept value.
5726
+ parameterSweep: WitanMetricsManifestParameterSweepSchema.optional(),
5727
+ // Provenance of the labels behind reportedSharpe (labelling-integrity, L1+artifact tier).
5728
+ labelSource: WitanMetricsManifestLabelSourceSchema.optional()
5729
+ }).strict();
5730
+
5731
+ // ../shared/src/schemas/witan-strategy-adapter.ts
5732
+ var WITAN_STRATEGY_ADAPTER_CONFIG_SCHEMA_VERSION = "witan-strategy-adapter-v0";
5733
+ var WitanStrategyAdapterConfigSchema = external_exports.object({
5734
+ schemaVersion: external_exports.literal(WITAN_STRATEGY_ADAPTER_CONFIG_SCHEMA_VERSION),
5735
+ // Argv for the strategy entrypoint, e.g. ["python3", "strategy.py"] or ["node", "run.mjs"].
5736
+ // Command-argv (not a language enum) is what keeps the adapter language-agnostic — most
5737
+ // real targets are Python, but the contract does not assume it.
5738
+ command: external_exports.array(external_exports.string().min(1).max(300)).min(1).max(20),
5739
+ // Relative path (within the target repo) to the data the strategy should backtest against.
5740
+ dataPath: external_exports.string().min(1).max(500).optional(),
5741
+ // Backtest window, as the target repo defines its own date strings (ISO-8601 recommended).
5742
+ // Optional — a strategy may declare its own default window instead.
5743
+ windowStart: external_exports.string().min(1).max(40).optional(),
5744
+ windowEnd: external_exports.string().min(1).max(40).optional(),
5745
+ // Relative path (within the sandboxed workspace) the entrypoint must write its raw result
5746
+ // to — see WitanStrategyRunResultSchema below.
5747
+ outputPath: external_exports.string().min(1).max(300).default("witan-run-result.json"),
5748
+ timeoutMs: external_exports.number().int().positive().max(30 * 60 * 1e3).default(2 * 60 * 1e3),
5749
+ // Explicit opt-in egress allowlist. Empty by default — no network from the sandboxed run
5750
+ // unless the target repo explicitly whitelists hosts here.
5751
+ allowedHosts: external_exports.array(external_exports.string().min(1).max(255)).max(20).default([])
5752
+ }).strict();
5753
+ var WitanStrategyRunResultSchema = WitanMetricsManifestSchema.omit({
5754
+ schemaVersion: true
5755
+ });
5756
+
5757
+ // ../shared/src/types/tactical-action.ts
5758
+ var TacticalActionTypeSchema = external_exports.enum([
5759
+ "merge_pr_auto_merge_safe",
5760
+ "deploy_staging",
5761
+ "acknowledge_canary_alert",
5762
+ "update_status_md",
5763
+ "provision_product",
5764
+ "dispatch_dev_task"
5765
+ ]);
5766
+ var TacticalActionStatusSchema = external_exports.enum(["executed", "blocked", "dry_run"]);
5767
+ var TacticalActionPolicySchema = external_exports.object({
5768
+ studioId: external_exports.string().uuid(),
5769
+ productId: external_exports.string().uuid(),
5770
+ actionType: TacticalActionTypeSchema,
5771
+ allowed: external_exports.boolean(),
5772
+ dryRun: external_exports.boolean(),
5773
+ promotedAt: external_exports.coerce.date().nullable(),
5774
+ forceEnableReason: external_exports.string().nullable()
5775
+ });
5776
+ var BaseActionSchema = external_exports.object({
5777
+ actionType: TacticalActionTypeSchema,
5778
+ productId: external_exports.string().uuid(),
5779
+ coordinatorSessionId: external_exports.string().uuid().nullable().optional(),
5780
+ customerFacingProduction: external_exports.boolean().default(false)
5781
+ });
5782
+ var MergePrTacticalActionSchema = BaseActionSchema.extend({
5783
+ actionType: external_exports.literal("merge_pr_auto_merge_safe"),
5784
+ repository: external_exports.string().min(1),
5785
+ pullRequestNumber: external_exports.number().int().positive(),
5786
+ labels: external_exports.array(external_exports.string()),
5787
+ ciStatus: external_exports.enum(["green", "red", "unknown"]),
5788
+ humanApprovals: external_exports.number().int().nonnegative(),
5789
+ coworkClaudeApprovedStatus: external_exports.enum(["green", "red", "unknown"]),
5790
+ // The PR head SHA captured at the SAME live precondition snapshot that
5791
+ // verified the label/CI/approvals/cowork-claude-approved gates. Threaded
5792
+ // through to the GitHub merge call as the conditional `sha` parameter so a
5793
+ // commit that lands between the snapshot and the merge (a fast follow-up
5794
+ // push, or a re-triggered CI run resetting status) can never be
5795
+ // squash-merged unverified — GitHub rejects the merge with 409 instead.
5796
+ headSha: external_exports.string().min(1)
5797
+ });
5798
+ var DeployStagingTacticalActionSchema = BaseActionSchema.extend({
5799
+ actionType: external_exports.literal("deploy_staging"),
5800
+ environment: external_exports.literal("staging"),
5801
+ mainStatus: external_exports.enum(["green", "red", "unknown"]),
5802
+ stagingStatus: external_exports.enum(["green", "red", "unknown"])
5803
+ });
5804
+ var AcknowledgeCanaryAlertTacticalActionSchema = BaseActionSchema.extend({
5805
+ actionType: external_exports.literal("acknowledge_canary_alert"),
5806
+ alertId: external_exports.string().min(1),
5807
+ fingerprint: external_exports.string().min(1),
5808
+ knownFalsePositiveFingerprints: external_exports.array(external_exports.string()).default([])
5809
+ });
5810
+ var UpdateStatusMdTacticalActionSchema = BaseActionSchema.extend({
5811
+ actionType: external_exports.literal("update_status_md"),
5812
+ source: external_exports.literal("daily_brief_synthesis"),
5813
+ statusMarkdown: external_exports.string().min(1).max(2e4)
5814
+ });
5815
+ var ProvisionProductTacticalActionSchema = BaseActionSchema.extend({
5816
+ actionType: external_exports.literal("provision_product"),
5817
+ productName: external_exports.string().min(1).max(80),
5818
+ vertical: external_exports.string().min(1).max(80),
5819
+ repoTarget: external_exports.string().min(1),
5820
+ templateRef: external_exports.string().min(1),
5821
+ scopeNotes: external_exports.string().max(2e3).optional()
5822
+ });
5823
+ var DispatchDevTaskTacticalActionSchema = BaseActionSchema.extend({
5824
+ actionType: external_exports.literal("dispatch_dev_task"),
5825
+ repo: RepoRefSchema,
5826
+ task: external_exports.string().min(1).max(2e4),
5827
+ executor: CodingAgentExecutorSchema
5828
+ });
5829
+ var TacticalActionSchema = external_exports.discriminatedUnion("actionType", [
5830
+ MergePrTacticalActionSchema,
5831
+ DeployStagingTacticalActionSchema,
5832
+ AcknowledgeCanaryAlertTacticalActionSchema,
5833
+ UpdateStatusMdTacticalActionSchema,
5834
+ ProvisionProductTacticalActionSchema,
5835
+ DispatchDevTaskTacticalActionSchema
5836
+ ]);
5837
+ var TacticalActionContextSchema = external_exports.object({
5838
+ studioId: external_exports.string().uuid(),
5839
+ actorUserId: external_exports.string().uuid().nullable().optional(),
5840
+ requestedAt: external_exports.coerce.date().optional()
5841
+ });
5842
+ var TacticalActionPermitResultSchema = external_exports.discriminatedUnion("status", [
5843
+ external_exports.object({
5844
+ status: external_exports.literal("executed"),
5845
+ auditLogId: external_exports.string().uuid(),
5846
+ reason: external_exports.literal("permit_allowed")
5847
+ }),
5848
+ external_exports.object({
5849
+ status: external_exports.literal("blocked"),
5850
+ auditLogId: external_exports.string().uuid().optional(),
5851
+ reason: external_exports.string().min(1)
5852
+ }),
5853
+ external_exports.object({
5854
+ status: external_exports.literal("dry_run"),
5855
+ auditLogId: external_exports.string().uuid(),
5856
+ reason: external_exports.literal("dry_run_mode"),
5857
+ wouldHaveDone: external_exports.record(external_exports.unknown())
5858
+ })
5859
+ ]);
5860
+ var TacticalActionLogEntrySchema = external_exports.object({
5861
+ id: external_exports.string().uuid(),
5862
+ studioId: external_exports.string().uuid(),
5863
+ productId: external_exports.string().uuid(),
5864
+ product: external_exports.string().nullable().optional(),
5865
+ coordinatorSessionId: external_exports.string().uuid().nullable(),
5866
+ actionType: TacticalActionTypeSchema.or(external_exports.string()),
5867
+ context: external_exports.record(external_exports.unknown()),
5868
+ permitResult: external_exports.record(external_exports.unknown()),
5869
+ status: TacticalActionStatusSchema,
5870
+ outcome: external_exports.string().nullable(),
5871
+ wouldHaveDone: external_exports.record(external_exports.unknown()).nullable(),
5872
+ attemptedAt: external_exports.string(),
5873
+ executedAt: external_exports.string().nullable()
5874
+ });
5875
+
5876
+ // ../shared/src/schemas/index.ts
5877
+ var StudioSchema = external_exports.object({
5878
+ id: external_exports.string().uuid(),
5879
+ name: external_exports.string().min(1).max(100),
5880
+ billingStatus: external_exports.enum(["trial", "active", "past_due", "cancelled"]),
5881
+ planTier: external_exports.enum(["trial", "solo", "studio", "studio_plus", "self_hosted", "enterprise"]),
5882
+ onPrem: external_exports.boolean().default(false),
5883
+ createdAt: external_exports.coerce.date()
5884
+ });
5885
+ var HumanSchema = external_exports.object({
5886
+ id: external_exports.string().uuid(),
5887
+ studioId: external_exports.string().uuid(),
5888
+ email: external_exports.string().email(),
5889
+ name: external_exports.string().min(1).max(120),
5890
+ oauthProviders: external_exports.array(external_exports.enum(["google", "microsoft", "apple"])).default([]),
5891
+ role: external_exports.enum(["owner", "member", "advisor", "per_product"]),
5892
+ timezone: external_exports.string(),
5893
+ briefTime: external_exports.string().regex(/^\d{2}:\d{2}$/),
5894
+ createdAt: external_exports.coerce.date()
5895
+ });
5896
+ var ProductSchema = external_exports.object({
5897
+ id: external_exports.string().uuid(),
5898
+ studioId: external_exports.string().uuid(),
5899
+ name: external_exports.string().min(1).max(80),
5900
+ slug: external_exports.string().regex(/^[a-z0-9][a-z0-9-]*[a-z0-9]$/),
5901
+ status: external_exports.enum(["active", "snoozed", "wound_down", "deleted"]),
5902
+ description: external_exports.string().max(280).optional(),
5903
+ repoUrl: external_exports.string().url().optional(),
5904
+ calendarTag: external_exports.string().optional(),
5905
+ emailLabelPrefix: external_exports.string().optional(),
5906
+ colorHex: external_exports.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
5907
+ scheduleBriefTime: external_exports.string().regex(/^\d{2}:\d{2}$/).optional(),
5908
+ briefTemplateId: external_exports.string().uuid().optional(),
5909
+ regulated: external_exports.boolean().default(false),
5910
+ createdAt: external_exports.coerce.date(),
5911
+ lastTouchedAt: external_exports.coerce.date()
5912
+ });
5913
+ var AgentSchema = external_exports.object({
5914
+ id: external_exports.string().uuid(),
5915
+ studioId: external_exports.string().uuid(),
5916
+ productId: external_exports.string().uuid(),
5917
+ agentType: external_exports.enum(["codex", "claude-code", "cowork", "cursor", "cline", "aider", "other"]),
5918
+ sessionId: external_exports.string().optional(),
5919
+ status: external_exports.enum(["active", "idle", "registered", "archived"]),
5920
+ registeredAt: external_exports.coerce.date(),
5921
+ lastActiveAt: external_exports.coerce.date().optional(),
5922
+ totalCostCentsLifetime: external_exports.number().int().nonnegative().default(0)
5923
+ });
5924
+ var AgentRunSchema = external_exports.object({
5925
+ id: external_exports.string().uuid(),
5926
+ agentId: external_exports.string().uuid(),
5927
+ coordinatorSessionId: external_exports.string().uuid().nullable().optional(),
5928
+ startedAt: external_exports.coerce.date(),
5929
+ completedAt: external_exports.coerce.date().optional(),
5930
+ actionType: external_exports.string(),
5931
+ toolName: external_exports.string().optional(),
5932
+ toolInputSummary: external_exports.string().optional(),
5933
+ toolOutputSummary: external_exports.string().optional(),
5934
+ tokensUsedInput: external_exports.number().int().nonnegative().default(0),
5935
+ tokensUsedOutput: external_exports.number().int().nonnegative().default(0),
5936
+ costCents: external_exports.number().int().nonnegative().default(0),
5937
+ durationMs: external_exports.number().int().nonnegative().default(0),
5938
+ resultStatus: external_exports.enum(["success", "failure", "partial", "timeout"]).optional(),
5939
+ resultSummary: external_exports.string().optional(),
5940
+ filesTouched: external_exports.array(external_exports.string()).default([])
5941
+ });
5942
+ var CoordinatorStatusSchema = external_exports.enum([
5943
+ "draft",
5944
+ "spawned",
5945
+ "in_progress",
5946
+ "reporting",
5947
+ "closed"
5948
+ ]);
5949
+ var CoordinatorEscalationPolicySchema = external_exports.object({
5950
+ stuck_after_hours: external_exports.number().int().positive(),
5951
+ method: external_exports.literal("flag_for_human"),
5952
+ urgency: external_exports.enum(["low", "medium", "high", "critical"]).optional()
5953
+ });
5954
+ var CoordinatorSessionSchema = external_exports.object({
5955
+ id: external_exports.string().uuid(),
5956
+ studioId: external_exports.string().uuid(),
5957
+ productId: external_exports.string().uuid(),
5958
+ promptId: external_exports.string().uuid().nullable(),
5959
+ objective: external_exports.string(),
5960
+ status: CoordinatorStatusSchema,
5961
+ spawnedAt: external_exports.coerce.date().nullable(),
5962
+ closedAt: external_exports.coerce.date().nullable(),
5963
+ escalationPolicy: CoordinatorEscalationPolicySchema.nullable(),
5964
+ outcomeSummary: external_exports.string().nullable(),
5965
+ createdAt: external_exports.coerce.date()
5966
+ });
5967
+ var AgentActivityWindowSchema = external_exports.enum(["24h", "7d", "30d"]);
5968
+ var AgentCostBucketSchema = external_exports.object({
5969
+ key: external_exports.string(),
5970
+ name: external_exports.string(),
5971
+ runCount: external_exports.number().int().nonnegative(),
5972
+ costUsd: external_exports.number().nonnegative()
5973
+ });
5974
+ var AgentCostRollupSchema = external_exports.object({
5975
+ window: AgentActivityWindowSchema,
5976
+ studioTotalUsd: external_exports.number().nonnegative(),
5977
+ runCount: external_exports.number().int().nonnegative(),
5978
+ byAgent: external_exports.array(AgentCostBucketSchema),
5979
+ byProduct: external_exports.array(AgentCostBucketSchema)
5980
+ });
5981
+ var AgentConflictRecordSchema = external_exports.object({
5982
+ file: external_exports.string(),
5983
+ agents: external_exports.array(external_exports.string()).min(2),
5984
+ references: external_exports.array(
5985
+ external_exports.object({
5986
+ runId: external_exports.string(),
5987
+ agent: external_exports.string(),
5988
+ product: external_exports.string().nullable(),
5989
+ startedAt: external_exports.string(),
5990
+ sessionRef: external_exports.string().nullable(),
5991
+ prUrl: external_exports.string().nullable()
5992
+ })
5993
+ )
5994
+ });
5995
+ var AgentLeaderboardEntrySchema = external_exports.object({
5996
+ agent: external_exports.string(),
5997
+ product: external_exports.string().nullable(),
5998
+ runCount: external_exports.number().int().nonnegative(),
5999
+ completedRuns: external_exports.number().int().nonnegative(),
6000
+ failedRuns: external_exports.number().int().nonnegative(),
6001
+ costUsd: external_exports.number().nonnegative(),
6002
+ filesTouched: external_exports.number().int().nonnegative(),
6003
+ conflicts: external_exports.number().int().nonnegative(),
6004
+ state: external_exports.enum(["productive", "idle", "duplicating"]),
6005
+ lastActiveAt: external_exports.string().nullable()
6006
+ });
6007
+ var ModelCostEstimateInputSchema = external_exports.object({
6008
+ model: external_exports.string().optional(),
6009
+ inputTokens: external_exports.number().int().nonnegative().default(0),
6010
+ outputTokens: external_exports.number().int().nonnegative().default(0)
6011
+ });
6012
+ var ModelCostEstimateSchema = external_exports.object({
6013
+ costUsd: external_exports.number().nonnegative(),
6014
+ method: external_exports.enum(["estimated_from_model_rate", "unknown_model_rate"]),
6015
+ rateModel: external_exports.enum(["claude-haiku", "claude-sonnet", "claude-opus", "unknown"])
6016
+ });
6017
+ var IntegrationSchema = external_exports.object({
6018
+ id: external_exports.string().uuid(),
6019
+ studioId: external_exports.string().uuid(),
6020
+ productId: external_exports.string().uuid().nullable(),
6021
+ kind: external_exports.string(),
6022
+ oauthTokenEncrypted: external_exports.string().optional(),
6023
+ refreshTokenEncrypted: external_exports.string().optional(),
6024
+ status: external_exports.enum(["active", "broken", "revoked"]),
6025
+ lastSyncAt: external_exports.coerce.date().optional(),
6026
+ errorCount: external_exports.number().int().nonnegative().default(0)
6027
+ });
6028
+ var EmailAccountSchema = external_exports.object({
6029
+ id: external_exports.string().uuid(),
6030
+ studioId: external_exports.string().uuid(),
6031
+ provider: external_exports.enum(["gmail", "m365", "imap"]),
6032
+ oauthTokenEncrypted: external_exports.string().optional(),
6033
+ defaultLabelPrefix: external_exports.string().optional(),
6034
+ triageCadenceMins: external_exports.number().int().positive().default(240)
6035
+ });
6036
+ var RoutineSchema = external_exports.object({
6037
+ id: external_exports.string().uuid(),
6038
+ productId: external_exports.string().uuid().nullable(),
6039
+ name: external_exports.string(),
6040
+ cronExpression: external_exports.string(),
6041
+ promptTemplate: external_exports.string(),
6042
+ model: external_exports.enum(["sonnet", "opus", "haiku"]).default("sonnet"),
6043
+ lastRunAt: external_exports.coerce.date().optional(),
6044
+ nextRunAt: external_exports.coerce.date().optional(),
6045
+ enabled: external_exports.boolean().default(true),
6046
+ retryCount: external_exports.number().int().nonnegative().default(0)
6047
+ });
6048
+ var RoutineRunSchema = external_exports.object({
6049
+ id: external_exports.string().uuid(),
6050
+ routineId: external_exports.string().uuid(),
6051
+ startedAt: external_exports.coerce.date(),
6052
+ completedAt: external_exports.coerce.date().optional(),
6053
+ status: external_exports.enum(["success", "failed", "retried"]),
6054
+ outputBriefId: external_exports.string().uuid().optional(),
6055
+ errorMessage: external_exports.string().optional(),
6056
+ inferenceCostCents: external_exports.number().int().nonnegative().default(0)
6057
+ });
6058
+ var BriefSchema = external_exports.object({
6059
+ id: external_exports.string().uuid(),
6060
+ studioId: external_exports.string().uuid(),
6061
+ generatedAt: external_exports.coerce.date(),
6062
+ type: external_exports.enum(["daily", "weekly", "on_demand"]),
6063
+ html: external_exports.string().optional(),
6064
+ markdown: external_exports.string(),
6065
+ sourceLinks: external_exports.array(external_exports.object({ label: external_exports.string(), url: external_exports.string() })).default([]),
6066
+ shared: external_exports.boolean().default(false),
6067
+ shareToken: external_exports.string().optional(),
6068
+ shareExpiresAt: external_exports.coerce.date().optional(),
6069
+ inferenceCostCents: external_exports.number().int().nonnegative().default(0)
6070
+ });
6071
+ var MemoryEntrySchema = external_exports.object({
6072
+ id: external_exports.string().uuid(),
6073
+ productId: external_exports.string().uuid(),
6074
+ kind: external_exports.enum(["decision", "fact", "customer", "status", "pattern", "thread"]),
6075
+ content: external_exports.string(),
6076
+ // embedding stored separately in pgvector; not modelled in API layer
6077
+ createdAt: external_exports.coerce.date(),
6078
+ lastReferencedAt: external_exports.coerce.date().optional(),
6079
+ referenceCount: external_exports.number().int().nonnegative().default(0),
6080
+ pinned: external_exports.boolean().default(false)
6081
+ });
6082
+ var InboxClassificationSchema = external_exports.object({
6083
+ id: external_exports.string().uuid(),
6084
+ emailAccountId: external_exports.string().uuid(),
6085
+ productId: external_exports.string().uuid().nullable(),
6086
+ messageId: external_exports.string(),
6087
+ sender: external_exports.string(),
6088
+ labelApplied: external_exports.string(),
6089
+ urgency: external_exports.enum(["needs_reply", "fyi", "noise", "escalation"]),
6090
+ confidence: external_exports.number().min(0).max(1),
6091
+ action: external_exports.enum(["labelled", "archived", "drafted_reply"]),
6092
+ userCorrected: external_exports.boolean().default(false),
6093
+ correctedTo: external_exports.string().optional()
6094
+ });
6095
+ var DraftReplySchema = external_exports.object({
6096
+ id: external_exports.string().uuid(),
6097
+ classificationId: external_exports.string().uuid(),
6098
+ draftText: external_exports.string(),
6099
+ confidence: external_exports.number().min(0).max(1),
6100
+ sentAt: external_exports.coerce.date().optional(),
6101
+ sentVia: external_exports.string().optional()
6102
+ });
6103
+ var AlwaysAlertRuleSchema = external_exports.object({
6104
+ id: external_exports.string().uuid(),
6105
+ studioId: external_exports.string().uuid(),
6106
+ ruleType: external_exports.enum(["sender", "subject", "domain"]),
6107
+ value: external_exports.string(),
6108
+ escalationMethod: external_exports.enum(["push", "slack", "email", "brief_pin"]).default("brief_pin")
6109
+ });
6110
+ var AuditEventSchema = external_exports.object({
6111
+ id: external_exports.string().uuid(),
6112
+ studioId: external_exports.string().uuid(),
6113
+ actor: external_exports.enum(["user", "agent", "system"]),
6114
+ action: external_exports.string(),
6115
+ integrationId: external_exports.string().uuid().optional(),
6116
+ productId: external_exports.string().uuid().optional(),
6117
+ timestamp: external_exports.coerce.date(),
6118
+ metadata: external_exports.record(external_exports.unknown()).default({})
6119
+ });
6120
+ var SubscriptionSchema = external_exports.object({
6121
+ id: external_exports.string().uuid(),
6122
+ studioId: external_exports.string().uuid(),
6123
+ stripeSubscriptionId: external_exports.string().optional(),
6124
+ tier: external_exports.enum(["trial", "solo", "studio", "studio_plus", "self_hosted", "enterprise"]),
6125
+ seats: external_exports.number().int().positive(),
6126
+ status: external_exports.string(),
6127
+ currentPeriodEnd: external_exports.coerce.date(),
6128
+ cancelAt: external_exports.coerce.date().optional()
6129
+ });
6130
+ var ProductMembershipSchema = external_exports.object({
6131
+ humanId: external_exports.string().uuid(),
6132
+ productId: external_exports.string().uuid(),
6133
+ role: external_exports.enum(["lead", "contributor", "observer"])
6134
+ });
6135
+
6136
+ // ../witan/src/batch.ts
6137
+ import { execFileSync as execFileSync3 } from "child_process";
6138
+ import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
6139
+ import { basename as basename2, join as join5 } from "path";
6140
+
6141
+ // ../witan/src/public-scan.ts
6142
+ import { resolve as resolve2 } from "path";
6143
+
6144
+ // ../witan/src/ingest.ts
6145
+ import { readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
6146
+ import { join as join2 } from "path";
6147
+
6148
+ // ../witan/src/finding-limits.ts
6149
+ var RULE_ID_MAX = 200;
6150
+ var MESSAGE_MAX = 500;
6151
+ var LOCATION_MAX = 700;
6152
+ function clamp(value, max) {
6153
+ if (value.length <= max) return value;
6154
+ const ellipsis = "...";
6155
+ return `${value.slice(0, Math.max(max - ellipsis.length, 0))}${ellipsis}`;
6156
+ }
6157
+ function clampFinding(finding) {
4725
6158
  return {
4726
- productSlug: options.productSlug,
4727
- productDisplayName: options.productDisplayName,
4728
- repo: {
4729
- path: options.repoPath,
4730
- ...headSha ? { headSha } : {}
4731
- },
4732
- generatedAt,
4733
- ...options.rubricVersion ? { rubricVersion: options.rubricVersion } : {},
4734
- archetype: archetype.archetype,
4735
- ...archetype.insufficientSourceReason ? { insufficientSourceReason: archetype.insufficientSourceReason } : {},
4736
- signals: [
4737
- ...collectRepoSignals(options.repoPath, generatedAt, repoFiles),
4738
- ...(options.domainCollectors ?? []).map((collect) => collect(options.repoPath, repoFiles)),
4739
- ...options.additionalSignals ?? []
4740
- ]
6159
+ ...finding,
6160
+ ruleId: clamp(finding.ruleId, RULE_ID_MAX),
6161
+ message: clamp(finding.message, MESSAGE_MAX),
6162
+ ...finding.location ? { location: clamp(finding.location, LOCATION_MAX) } : {}
4741
6163
  };
4742
6164
  }
4743
- var SOURCE_EXTENSION_PATTERN = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|go|rs|java|kt|kts|rb|php|swift|cpp|cc|cxx|c|h|hpp|cs|scala|dart|ex|exs)$/i;
4744
- var BUNDLED_BINARY_EXTENSION_PATTERN = /\.(vsix|zip|tgz|whl|exe|dmg|pkg|msi|jar|war|apk|ipa|dll|dylib)$/i;
4745
- var DOCS_OR_MEDIA_EXTENSION_PATTERN = /\.(md|mdx|rst|png|jpe?g|gif|svg|webp|ico|pdf)$/i;
4746
- var INGEST_POINTER = "To assess a closed/bundled tool, ingest its scanner output via --ingest <sarif|scorecard>.";
4747
- function classifyRepoArchetype(repoFiles) {
4748
- const totalFileCount = repoFiles.length;
4749
- const sourceFileCount = repoFiles.filter((file) => SOURCE_EXTENSION_PATTERN.test(file)).length;
4750
- if (totalFileCount === 0) {
4751
- return {
4752
- archetype: "empty",
4753
- sourceFileCount: 0,
4754
- totalFileCount: 0,
4755
- insufficientSourceReason: "Repository has no tracked files \u2014 there is nothing to certify."
4756
- };
4757
- }
4758
- if (sourceFileCount > 0) {
4759
- return {
4760
- archetype: isLikelyMonorepo(repoFiles) ? "monorepo" : "source",
4761
- sourceFileCount,
4762
- totalFileCount
4763
- };
4764
- }
4765
- const binaryFiles = repoFiles.filter((file) => BUNDLED_BINARY_EXTENSION_PATTERN.test(file));
6165
+
6166
+ // ../witan/src/generic-adapter.ts
6167
+ import { readFileSync as readFileSync2 } from "fs";
6168
+
6169
+ // ../witan/src/json-safe.ts
6170
+ function stripBom(text) {
6171
+ return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
6172
+ }
6173
+
6174
+ // ../witan/src/generic-adapter.ts
6175
+ var VALID_DIMENSIONS = /* @__PURE__ */ new Set([
6176
+ "A1",
6177
+ "A2",
6178
+ "A3",
6179
+ "A4",
6180
+ "A5",
6181
+ "B1",
6182
+ "B2",
6183
+ "B3",
6184
+ "B4",
6185
+ "B5",
6186
+ "B6"
6187
+ ]);
6188
+ var VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "warning", "info"]);
6189
+ function isGenericSignalDocument(raw) {
6190
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
6191
+ const doc = raw;
6192
+ return typeof doc.tool === "string" && doc.tool.length > 0 && Array.isArray(doc.signals);
6193
+ }
6194
+ function parseGenericJson(raw) {
6195
+ if (!isGenericSignalDocument(raw)) return [];
6196
+ const source = raw.tool ?? "unknown";
6197
+ const signals = [];
6198
+ for (const signal of raw.signals ?? []) {
6199
+ const dimension = signal.dimension;
6200
+ if (!dimension || !VALID_DIMENSIONS.has(dimension)) continue;
6201
+ const weight = typeof signal.weight === "number" && Number.isFinite(signal.weight) ? Math.min(Math.max(signal.weight, 0), 1) : 0.5;
6202
+ const findings = [];
6203
+ for (const finding of signal.findings ?? []) {
6204
+ const severity = finding.severity;
6205
+ if (!severity || !VALID_SEVERITIES.has(severity)) continue;
6206
+ const ruleId = finding.ruleId ?? "unknown";
6207
+ findings.push({
6208
+ ruleId,
6209
+ severity,
6210
+ message: finding.message ?? ruleId,
6211
+ ...finding.location ? { location: finding.location } : {}
6212
+ });
6213
+ }
6214
+ if (findings.length === 0) continue;
6215
+ signals.push({ source, dimension, weight, findings });
6216
+ }
6217
+ return signals;
6218
+ }
6219
+
6220
+ // ../witan/src/sarif-adapter.ts
6221
+ import { readFileSync as readFileSync3 } from "fs";
6222
+ var DIMENSION_RULES = [
6223
+ // A2 — injection / secrets / auth (high weight)
6224
+ {
6225
+ pattern: /sql[- _]?inject|cwe-89|sqli|command[- _]?inject|cwe-78|os[- _]?cmd|rce|remote[- _]?code/i,
6226
+ dimension: "A2",
6227
+ weight: 0.9
6228
+ },
6229
+ {
6230
+ pattern: /secret|credential|api[- _]?key|password[- _]?leak|hardcoded[- _]?(token|key|secret)|cwe-259|cwe-798/i,
6231
+ dimension: "A2",
6232
+ weight: 0.9
6233
+ },
6234
+ {
6235
+ pattern: /path[- _]?traversal|directory[- _]?traversal|cwe-22|lfi|rfi/i,
6236
+ dimension: "A2",
6237
+ weight: 0.8
6238
+ },
6239
+ {
6240
+ pattern: /ssrf|server[- _]?side[- _]?request[- _]?forg|cwe-918/i,
6241
+ dimension: "A2",
6242
+ weight: 0.8
6243
+ },
6244
+ {
6245
+ pattern: /xxe|xml[- _]?external[- _]?entity|cwe-611/i,
6246
+ dimension: "A2",
6247
+ weight: 0.8
6248
+ },
6249
+ {
6250
+ pattern: /auth[- _]?bypass|broken[- _]?auth|missing[- _]?auth|cwe-287|cwe-306|cwe-863|cwe-862/i,
6251
+ dimension: "A2",
6252
+ weight: 0.7
6253
+ },
6254
+ {
6255
+ pattern: /xss|cross[- _]?site[- _]?script|cwe-79/i,
6256
+ dimension: "A2",
6257
+ weight: 0.6
6258
+ },
6259
+ // A4 — dependency vulnerabilities
6260
+ {
6261
+ pattern: /cve-\d{4}-\d+|supply[- _]?chain|depend[a-z]*[- _]?vulner|outdated[- _]?depend|deprecated[- _]?package/i,
6262
+ dimension: "A4",
6263
+ weight: 0.8
6264
+ },
6265
+ {
6266
+ pattern: /npm[- _]?audit|yarn[- _]?audit|pnpm[- _]?audit|pip[- _]?audit|cargo[- _]?audit/i,
6267
+ dimension: "A4",
6268
+ weight: 0.7
6269
+ },
6270
+ // A3 — production readiness / insecure config
6271
+ {
6272
+ pattern: /insecure[- _]?tls|weak[- _]?cipher|tls[- _]?version|http[- _]?strict|hsts|csp[- _]?missing|cwe-326|cwe-327/i,
6273
+ dimension: "A3",
6274
+ weight: 0.5
6275
+ },
6276
+ {
6277
+ pattern: /error[- _]?handl|unhandled[- _]?exception|crash|uncaught|stacktrace[- _]?leak/i,
6278
+ dimension: "A3",
6279
+ weight: 0.4
6280
+ },
6281
+ {
6282
+ pattern: /debug[- _]?mode|dev[- _]?endpoint|staging[- _]?secret/i,
6283
+ dimension: "A3",
6284
+ weight: 0.4
6285
+ },
6286
+ // A1 — test integrity (low weight; SARIF rarely surfaces this)
6287
+ {
6288
+ pattern: /skipped[- _]?test|hollow[- _]?test|empty[- _]?assert|no[- _]?assertion/i,
6289
+ dimension: "A1",
6290
+ weight: 0.2
6291
+ }
6292
+ ];
6293
+ var DEFAULT_DIMENSION = "A2";
6294
+ var DEFAULT_WEIGHT = 0.3;
6295
+ var DROP_LEVEL = /* @__PURE__ */ new Set(["none"]);
6296
+ function mapLevel(level) {
6297
+ if (!level || DROP_LEVEL.has(level)) return null;
6298
+ if (level === "error") return "critical";
6299
+ if (level === "warning") return "warning";
6300
+ return "info";
6301
+ }
6302
+ function mapRuleToDimension(ruleId, rules) {
6303
+ for (const rule of rules) {
6304
+ if (rule.pattern.test(ruleId)) {
6305
+ return { dimension: rule.dimension, weight: rule.weight };
6306
+ }
6307
+ }
6308
+ return { dimension: DEFAULT_DIMENSION, weight: DEFAULT_WEIGHT };
6309
+ }
6310
+ function renderLocation(result) {
6311
+ const loc = result.locations?.[0]?.physicalLocation;
6312
+ if (!loc) return void 0;
6313
+ const uri = loc.artifactLocation?.uri;
6314
+ const line = loc.region?.startLine;
6315
+ if (!uri) return void 0;
6316
+ return line != null ? `${uri}:${line}` : uri;
6317
+ }
6318
+ function dedupeFindings(findings) {
6319
+ const seen = /* @__PURE__ */ new Set();
6320
+ return findings.filter((f) => {
6321
+ const key = `${f.ruleId}|${f.severity}|${f.location ?? ""}`;
6322
+ if (seen.has(key)) return false;
6323
+ seen.add(key);
6324
+ return true;
6325
+ });
6326
+ }
6327
+ function parseSarifJson(raw, extraDimensionRules) {
6328
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return [];
6329
+ const dimensionRules = extraDimensionRules ? [...DIMENSION_RULES, ...extraDimensionRules] : DIMENSION_RULES;
6330
+ const doc = raw;
6331
+ if (!Array.isArray(doc.runs)) return [];
6332
+ const buckets = /* @__PURE__ */ new Map();
6333
+ for (const run of doc.runs) {
6334
+ const driverName = run.tool?.driver?.name ?? "unknown";
6335
+ const source = `sarif:${driverName}`;
6336
+ const ruleDefaultLevels = /* @__PURE__ */ new Map();
6337
+ for (const rule of run.tool?.driver?.rules ?? []) {
6338
+ if (rule.id && rule.defaultConfiguration?.level) {
6339
+ ruleDefaultLevels.set(rule.id, rule.defaultConfiguration.level);
6340
+ }
6341
+ }
6342
+ for (const result of run.results ?? []) {
6343
+ const ruleId = result.ruleId ?? "unknown";
6344
+ const effectiveLevel = result.level ?? ruleDefaultLevels.get(ruleId) ?? "warning";
6345
+ const severity = mapLevel(effectiveLevel);
6346
+ if (!severity) continue;
6347
+ const message = result.message?.text ?? ruleId;
6348
+ const location = renderLocation(result);
6349
+ const { dimension, weight } = mapRuleToDimension(ruleId, dimensionRules);
6350
+ const bucketKey = `${source}|${dimension}`;
6351
+ if (!buckets.has(bucketKey)) {
6352
+ buckets.set(bucketKey, { source, dimension, weight, findings: [] });
6353
+ }
6354
+ buckets.get(bucketKey)?.findings.push({ ruleId, severity, message, ...location ? { location } : {} });
6355
+ }
6356
+ }
6357
+ return Array.from(buckets.values()).map(({ source, dimension, weight, findings }) => ({
6358
+ source,
6359
+ dimension,
6360
+ weight,
6361
+ findings: dedupeFindings(findings)
6362
+ }));
6363
+ }
6364
+
6365
+ // ../witan/src/scorecard-adapter.ts
6366
+ import { readFileSync as readFileSync4 } from "fs";
6367
+ var CHECK_RULES = {
6368
+ // B2 — PR outcome traceability
6369
+ "Branch-Protection": { dimension: "B2", weight: 0.8 },
6370
+ "Code-Review": { dimension: "B2", weight: 0.7 },
6371
+ // B3 — CI and QA discipline
6372
+ "Dangerous-Workflow": { dimension: "B3", weight: 0.9 },
6373
+ "Token-Permissions": { dimension: "B3", weight: 0.8 },
6374
+ "CI-Tests": { dimension: "B3", weight: 0.7 },
6375
+ Maintained: { dimension: "B3", weight: 0.3 },
6376
+ // B4 — audit trail / release integrity
6377
+ "Signed-Releases": { dimension: "B4", weight: 0.7 },
6378
+ Packaging: { dimension: "B4", weight: 0.4 },
6379
+ "Security-Policy": { dimension: "B4", weight: 0.4 },
6380
+ // A4 — dependency hygiene
6381
+ Vulnerabilities: { dimension: "A4", weight: 0.9 },
6382
+ "Pinned-Dependencies": { dimension: "A4", weight: 0.8 },
6383
+ "Dependency-Update-Tool": { dimension: "A4", weight: 0.6 },
6384
+ // A2 — data-layer / static analysis
6385
+ SAST: { dimension: "A2", weight: 0.7 },
6386
+ "Binary-Artifacts": { dimension: "A2", weight: 0.6 },
6387
+ // A1 — test integrity
6388
+ Fuzzing: { dimension: "A1", weight: 0.3 }
6389
+ };
6390
+ function mapScoreToSeverity(score) {
6391
+ if (score == null || Number.isNaN(score)) return null;
6392
+ if (score < 0) return null;
6393
+ if (score >= 10) return null;
6394
+ if (score <= 3) return "critical";
6395
+ if (score <= 6) return "warning";
6396
+ return "info";
6397
+ }
6398
+ function dedupeFindings2(findings) {
6399
+ const seen = /* @__PURE__ */ new Set();
6400
+ return findings.filter((f) => {
6401
+ const key = `${f.ruleId}|${f.severity}|${f.location ?? ""}`;
6402
+ if (seen.has(key)) return false;
6403
+ seen.add(key);
6404
+ return true;
6405
+ });
6406
+ }
6407
+ function parseScorecardJson(raw) {
6408
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return [];
6409
+ const doc = raw;
6410
+ if (!Array.isArray(doc.checks)) return [];
6411
+ const buckets = /* @__PURE__ */ new Map();
6412
+ for (const check of doc.checks) {
6413
+ const name = check.name;
6414
+ if (!name) continue;
6415
+ const rule = CHECK_RULES[name];
6416
+ if (!rule) continue;
6417
+ const severity = mapScoreToSeverity(check.score);
6418
+ if (!severity) continue;
6419
+ const scoreText = typeof check.score === "number" ? `${check.score}/10` : "n/a";
6420
+ const message = `${name} (Scorecard ${scoreText}): ${check.reason ?? "weakness detected"}`.slice(0, 500);
6421
+ const location = check.documentation?.url;
6422
+ const bucket = buckets.get(rule.dimension) ?? { weight: 0, findings: [] };
6423
+ bucket.weight = Math.max(bucket.weight, rule.weight);
6424
+ bucket.findings.push({
6425
+ ruleId: `scorecard:${name}`,
6426
+ severity,
6427
+ message,
6428
+ ...location ? { location } : {}
6429
+ });
6430
+ buckets.set(rule.dimension, bucket);
6431
+ }
6432
+ return Array.from(buckets.entries()).map(([dimension, { weight, findings }]) => ({
6433
+ source: "scorecard",
6434
+ dimension,
6435
+ weight,
6436
+ findings: dedupeFindings2(findings)
6437
+ }));
6438
+ }
6439
+
6440
+ // ../witan/src/ingest.ts
6441
+ function looksLikeSarif(raw) {
6442
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
6443
+ return Array.isArray(raw.runs);
6444
+ }
6445
+ function looksLikeScorecard(raw) {
6446
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
6447
+ return Array.isArray(raw.checks);
6448
+ }
6449
+ function countRawEntries(raw) {
6450
+ const obj = raw;
6451
+ if (Array.isArray(obj.runs)) {
6452
+ return obj.runs.reduce((sum, run) => {
6453
+ const results = run.results;
6454
+ return sum + (Array.isArray(results) ? results.length : 0);
6455
+ }, 0);
6456
+ }
6457
+ if (Array.isArray(obj.checks)) return obj.checks.length;
6458
+ if (Array.isArray(obj.signals)) {
6459
+ return obj.signals.reduce((sum, signal) => {
6460
+ const findings = signal.findings;
6461
+ return sum + (Array.isArray(findings) ? findings.length : 0);
6462
+ }, 0);
6463
+ }
6464
+ return 0;
6465
+ }
6466
+ function rawToolName(raw) {
6467
+ const obj = raw;
6468
+ if (Array.isArray(obj.runs)) {
6469
+ const firstRun = obj.runs[0];
6470
+ return firstRun?.tool?.driver?.name ?? "unknown";
6471
+ }
6472
+ if (typeof obj.tool === "string" && obj.tool.length > 0) return obj.tool;
6473
+ return "scorecard";
6474
+ }
6475
+ function parseIngestFile(filePath) {
6476
+ let raw;
6477
+ try {
6478
+ raw = JSON.parse(stripBom(readFileSync5(filePath, "utf8")));
6479
+ } catch (error) {
6480
+ const message = error instanceof Error ? error.message : String(error);
6481
+ throw new Error(`Cejel: could not parse ingest file as JSON: ${filePath} (${message})`);
6482
+ }
6483
+ let signals;
6484
+ if (looksLikeSarif(raw)) signals = parseSarifJson(raw);
6485
+ else if (looksLikeScorecard(raw)) signals = parseScorecardJson(raw);
6486
+ else if (isGenericSignalDocument(raw)) signals = parseGenericJson(raw);
6487
+ if (!signals) {
6488
+ throw new Error(
6489
+ `Cejel: unrecognized ingest file format: ${filePath} \u2014 expected SARIF (a "runs" array), OpenSSF Scorecard JSON (a "checks" array), or the generic Cejel external-signal shape (a "tool" string + "signals" array). See the cejel README "Aggregate your scanners".`
6490
+ );
6491
+ }
6492
+ const rawCount = countRawEntries(raw);
6493
+ const mappedCount = signals.reduce((sum, signal) => sum + signal.findings.length, 0);
6494
+ if (rawCount > 0 && mappedCount === 0) {
6495
+ process.stderr.write(
6496
+ `Cejel: ${rawCount} findings from ${rawToolName(raw)} (${filePath}), 0 mapped to trust criteria
6497
+ `
6498
+ );
6499
+ }
6500
+ return signals.map((signal) => ({
6501
+ ...signal,
6502
+ findings: signal.findings.map(clampFinding)
6503
+ }));
6504
+ }
6505
+ function expandIngestPattern(pattern) {
6506
+ if (!pattern.includes("*")) return [pattern];
6507
+ const lastSlash = pattern.lastIndexOf("/");
6508
+ const dir = lastSlash === -1 ? "." : pattern.slice(0, lastSlash);
6509
+ const filePattern = lastSlash === -1 ? pattern : pattern.slice(lastSlash + 1);
6510
+ const regexSource = filePattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
6511
+ const regex = new RegExp(`^${regexSource}$`);
6512
+ let entries;
6513
+ try {
6514
+ entries = readdirSync2(dir);
6515
+ } catch {
6516
+ return [];
6517
+ }
6518
+ return entries.filter((name) => regex.test(name)).sort().map((name) => join2(dir, name));
6519
+ }
6520
+ function discoverIngestInputs(repoPath) {
6521
+ const inputsDir = join2(repoPath, ".cejel", "inputs");
6522
+ let entries;
6523
+ try {
6524
+ entries = readdirSync2(inputsDir);
6525
+ } catch {
6526
+ return [];
6527
+ }
6528
+ return entries.filter((name) => /\.(sarif|json)$/i.test(name)).sort().map((name) => join2(inputsDir, name));
6529
+ }
6530
+
6531
+ // ../witan/src/repo-signals.ts
6532
+ import { execFileSync } from "child_process";
6533
+ import { createHash } from "crypto";
6534
+ import { readFileSync as readFileSync6, readdirSync as readdirSync3, realpathSync, statSync as statSync2 } from "fs";
6535
+ import { basename, join as join3, relative, resolve, sep } from "path";
6536
+ function buildWitanInputFromRepo(options) {
6537
+ assertRepoPathExists(options.repoPath);
6538
+ const generatedAt = options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
6539
+ const headSha = readGitHead(options.repoPath);
6540
+ const repoFiles = listRepoFiles(options.repoPath);
6541
+ const archetype = classifyRepoArchetype(repoFiles);
6542
+ return {
6543
+ productSlug: options.productSlug,
6544
+ productDisplayName: options.productDisplayName,
6545
+ repo: {
6546
+ path: options.repoPath,
6547
+ ...headSha ? { headSha } : {}
6548
+ },
6549
+ generatedAt,
6550
+ ...options.rubricVersion ? { rubricVersion: options.rubricVersion } : {},
6551
+ archetype: archetype.archetype,
6552
+ ...archetype.insufficientSourceReason ? { insufficientSourceReason: archetype.insufficientSourceReason } : {},
6553
+ signals: [
6554
+ ...collectRepoSignals(options.repoPath, generatedAt, repoFiles),
6555
+ ...(options.domainCollectors ?? []).map((collect) => collect(options.repoPath, repoFiles)),
6556
+ ...options.additionalSignals ?? []
6557
+ ]
6558
+ };
6559
+ }
6560
+ var SOURCE_LANGUAGES = [
6561
+ {
6562
+ name: "JS/TS",
6563
+ tier: "deep",
6564
+ extensions: ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"]
6565
+ },
6566
+ { name: "Python", tier: "deep", extensions: ["py"] },
6567
+ { name: "Go", tier: "partial", extensions: ["go"] },
6568
+ { name: "Rust", tier: "partial", extensions: ["rs"] },
6569
+ { name: "Java", tier: "partial", extensions: ["java"] },
6570
+ { name: "Ruby", tier: "partial", extensions: ["rb"] },
6571
+ { name: "PHP", tier: "partial", extensions: ["php"] },
6572
+ { name: "C#", tier: "partial", extensions: ["cs"] },
6573
+ { name: "C/C++", tier: "partial", extensions: ["cpp", "cc", "cxx", "c", "h", "hpp"] },
6574
+ { name: "Swift", tier: "partial", extensions: ["swift"] },
6575
+ { name: "Kotlin", tier: "partial", extensions: ["kt", "kts"] },
6576
+ { name: "Dart", tier: "partial", extensions: ["dart"] },
6577
+ { name: "Elixir", tier: "partial", extensions: ["ex", "exs"] },
6578
+ { name: "Scala", tier: "partial", extensions: ["scala"] },
6579
+ { name: "shell", tier: "unmodelled", extensions: ["sh", "bash", "zsh"] },
6580
+ { name: "R", tier: "unmodelled", extensions: ["r"] },
6581
+ { name: "Lua", tier: "unmodelled", extensions: ["lua"] },
6582
+ { name: "Julia", tier: "unmodelled", extensions: ["jl"] },
6583
+ { name: "Haskell", tier: "unmodelled", extensions: ["hs"] },
6584
+ { name: "Terraform", tier: "unmodelled", extensions: ["tf", "tfvars"] },
6585
+ { name: "SQL", tier: "unmodelled", extensions: ["sql"] },
6586
+ { name: "Perl", tier: "unmodelled", extensions: ["pl", "pm"] },
6587
+ { name: "OCaml", tier: "unmodelled", extensions: ["ml", "mli"] },
6588
+ { name: "Clojure", tier: "unmodelled", extensions: ["clj", "cljs", "cljc"] },
6589
+ { name: "Erlang", tier: "unmodelled", extensions: ["erl", "hrl"] },
6590
+ { name: "Nim", tier: "unmodelled", extensions: ["nim"] },
6591
+ { name: "Zig", tier: "unmodelled", extensions: ["zig"] },
6592
+ { name: "F#", tier: "unmodelled", extensions: ["fs", "fsx"] },
6593
+ { name: "Groovy", tier: "unmodelled", extensions: ["groovy", "gvy"] }
6594
+ ];
6595
+ var SOURCE_EXTENSION_PATTERN = new RegExp(
6596
+ `\\.(${SOURCE_LANGUAGES.flatMap((language) => language.extensions).join("|")})$`,
6597
+ "i"
6598
+ );
6599
+ var BUNDLED_BINARY_EXTENSION_PATTERN = /\.(vsix|zip|tgz|whl|exe|dmg|pkg|msi|jar|war|apk|ipa|dll|dylib)$/i;
6600
+ var DOCS_OR_MEDIA_EXTENSION_PATTERN = /\.(md|mdx|rst|png|jpe?g|gif|svg|webp|ico|pdf)$/i;
6601
+ var KNOWN_NON_SOURCE_EXTENSION_PATTERN = /\.(json|jsonc|json5|ya?ml|toml|txt|lock|xml|csv|tsv|ini|cfg|conf|properties|env|sum|mod|graphql|gql|proto|editorconfig|gitignore|gitattributes|npmrc|nvmrc|dockerignore|log|txt\.license)$/i;
6602
+ var INGEST_POINTER = "To assess a closed/bundled tool, ingest its scanner output via --ingest <sarif|scorecard>.";
6603
+ function classifyRepoArchetype(repoFiles) {
6604
+ const totalFileCount = repoFiles.length;
6605
+ const sourceFileCount = repoFiles.filter((file) => SOURCE_EXTENSION_PATTERN.test(file)).length;
6606
+ if (totalFileCount === 0) {
6607
+ return {
6608
+ archetype: "empty",
6609
+ sourceFileCount: 0,
6610
+ totalFileCount: 0,
6611
+ insufficientSourceReason: "Repository has no tracked files \u2014 there is nothing to certify."
6612
+ };
6613
+ }
6614
+ if (sourceFileCount > 0) {
6615
+ return {
6616
+ archetype: isLikelyMonorepo(repoFiles) ? "monorepo" : "source",
6617
+ sourceFileCount,
6618
+ totalFileCount
6619
+ };
6620
+ }
6621
+ const binaryFiles = repoFiles.filter((file) => BUNDLED_BINARY_EXTENSION_PATTERN.test(file));
4766
6622
  if (binaryFiles.length > 0) {
4767
6623
  const sample = binaryFiles[0];
4768
6624
  return {
@@ -4781,8 +6637,32 @@ function classifyRepoArchetype(repoFiles) {
4781
6637
  insufficientSourceReason: `${sourceFileCount} source file(s) found among ${totalFileCount} tracked file(s); repo appears to be a docs/distribution tree (README/markdown/media), not a source tree \u2014 cejel rates source, not docs. ${INGEST_POINTER}`
4782
6638
  };
4783
6639
  }
6640
+ const unrecognisedExtensions = collectUnrecognisedSourceExtensions(repoFiles);
6641
+ if (unrecognisedExtensions.length > 0) {
6642
+ const sample = unrecognisedExtensions.slice(0, 6).join(", ");
6643
+ return {
6644
+ archetype: "unrecognised_ecosystem",
6645
+ sourceFileCount,
6646
+ totalFileCount,
6647
+ insufficientSourceReason: `Cejel does not yet read this repository's source language(s) (${sample}) \u2014 0 of ${totalFileCount} tracked file(s) matched a recognised source extension. Cejel abstains from a verdict rather than score a language it cannot parse; the Criterion Profile and Measured coverage below show exactly which dimensions were and were not measured. ${INGEST_POINTER}`
6648
+ };
6649
+ }
4784
6650
  return { archetype: "source", sourceFileCount, totalFileCount };
4785
6651
  }
6652
+ function collectUnrecognisedSourceExtensions(repoFiles) {
6653
+ const found = /* @__PURE__ */ new Set();
6654
+ for (const file of repoFiles) {
6655
+ const base = basename(file);
6656
+ const match = /\.([a-zA-Z0-9_]+)$/.exec(base);
6657
+ const ext = match?.[1];
6658
+ if (!ext) continue;
6659
+ if (KNOWN_NON_SOURCE_EXTENSION_PATTERN.test(base)) continue;
6660
+ if (DOCS_OR_MEDIA_EXTENSION_PATTERN.test(base)) continue;
6661
+ if (BUNDLED_BINARY_EXTENSION_PATTERN.test(base)) continue;
6662
+ found.add(`.${ext.toLowerCase()}`);
6663
+ }
6664
+ return [...found].sort();
6665
+ }
4786
6666
  function isLikelyMonorepo(repoFiles) {
4787
6667
  const packageRoots = new Set(
4788
6668
  repoFiles.filter((file) => /(^|\/)package\.json$/.test(file)).map((file) => file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "")
@@ -4792,7 +6672,7 @@ function isLikelyMonorepo(repoFiles) {
4792
6672
  function assertRepoPathExists(repoPath) {
4793
6673
  let stats;
4794
6674
  try {
4795
- stats = statSync(repoPath);
6675
+ stats = statSync2(repoPath);
4796
6676
  } catch {
4797
6677
  throw new Error(`Cejel: path not found: ${repoPath}`);
4798
6678
  }
@@ -4820,14 +6700,14 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles) {
4820
6700
  const a5Signal = collectA5ClaimRealityEvidence(repoPath, repoFiles);
4821
6701
  const b1Signal = buildNotApplicableSignal(
4822
6702
  "B1",
4823
- "Dispatch-trace process signal is not evaluated for external repos."
6703
+ "Repository scans do not evaluate the dispatch-trace process dimension."
4824
6704
  );
4825
6705
  const b2Signal = collectB2PrTraceEvidence(repoPath, repoFiles);
4826
6706
  const b3Signal = collectB3CiDisciplineEvidence(repoPath, repoFiles, mono);
4827
6707
  const b4Signal = collectB4AuditEvidence(repoPath, repoFiles, generatedAt);
4828
6708
  const b5Signal = buildNotApplicableSignal(
4829
6709
  "B5",
4830
- "Learning-trace process signal is not evaluated for external repos."
6710
+ "Repository scans do not evaluate the learning-trace process dimension."
4831
6711
  );
4832
6712
  const b6Signal = collectB6PrivilegedOpsGatingEvidence(repoPath, repoFiles);
4833
6713
  for (const signal of [
@@ -4860,7 +6740,7 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles) {
4860
6740
  const runnerFiles = repoFiles.filter(isTestRunnerConfig).slice(0, 6);
4861
6741
  const coverageFiles = repoFiles.filter(isCoverageConfig).slice(0, 4);
4862
6742
  const packageJson = findRootPackageJson(repoFiles);
4863
- const packageScripts = packageJson ? readPackageScripts(join(repoPath, packageJson)) : /* @__PURE__ */ new Map();
6743
+ const packageScripts = packageJson ? readPackageScripts(join3(repoPath, packageJson)) : /* @__PURE__ */ new Map();
4864
6744
  const coveragePercent = readCoveragePercent(repoPath, coverageFiles);
4865
6745
  const ciWorkflows = repoFiles.filter(isCiWorkflow);
4866
6746
  const ciTestWorkflow = ciWorkflows.find(
@@ -5014,8 +6894,7 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles) {
5014
6894
  };
5015
6895
  }
5016
6896
  function collectA2IsolationEvidence(repoPath, repoFiles) {
5017
- const alfredEvidence = [];
5018
- const evidence = [...alfredEvidence];
6897
+ const evidence = [];
5019
6898
  const findings = [];
5020
6899
  const gitignore = repoFiles.find((file) => basename(file) === ".gitignore");
5021
6900
  const envExamples = repoFiles.filter((file) => isEnvTemplatePath(file));
@@ -5031,47 +6910,43 @@ function collectA2IsolationEvidence(repoPath, repoFiles) {
5031
6910
  repoPath,
5032
6911
  repoFiles.filter(isImplementationFile).slice(0, 60)
5033
6912
  );
5034
- if (alfredEvidence.length === 0) {
5035
- const implFiles = repoFiles.filter(isImplementationFile);
5036
- const gitignoreHasEnvRule = gitignore != null && fileContains(repoPath, gitignore, /^\.env(\*|\b)|\.env\./m);
5037
- const committedEnvFilePath = repoFiles.find(
5038
- (f) => /(?:^|\/)\.env(?:\.|$)/i.test(f) && !isEnvTemplatePath(f)
6913
+ const implFiles = repoFiles.filter(isImplementationFile);
6914
+ const gitignoreHasEnvRule = gitignore != null && fileContains(repoPath, gitignore, /^\.env(\*|\b)|\.env\./m);
6915
+ const committedEnvFilePath = repoFiles.find(
6916
+ (f) => /(?:^|\/)\.env(?:\.|$)/i.test(f) && !isEnvTemplatePath(f)
6917
+ );
6918
+ const hasEnvInHistory = committedEnvFilePath === void 0 && hasEnvPathInGitHistory(repoPath);
6919
+ const dbLayerImportFile = migrationFiles.length === 0 ? implFiles.slice(0, 30).find((f) => fileContains(repoPath, f, DB_CLIENT_PATTERN)) : void 0;
6920
+ const hasDataLayer = migrationFiles.length > 0 || dbLayerImportFile !== void 0;
6921
+ const hasSecretsSurface = envExamples.length > 0 || committedEnvFilePath !== void 0 || gitignoreHasEnvRule || hasEnvInHistory;
6922
+ if (!hasDataLayer && !hasSecretsSurface && !hasConfirmedSecretFinding && !cryptoHygiene.hasSurface) {
6923
+ return buildNotApplicableSignal(
6924
+ "A2",
6925
+ "No data layer (DB/ORM/migrations) or ratable secrets surface detected \u2014 A2 not applicable to this repo archetype. A ratable surface requires .env* files, .gitignore .env rule, committed/history .env path, or detected signing/HMAC/secret-comparison code; bare env reads (process.env / os.environ / std::env::) do not qualify."
6926
+ );
6927
+ }
6928
+ if (committedEnvFilePath) {
6929
+ evidence.push(
6930
+ evidenceForRelative(
6931
+ repoPath,
6932
+ committedEnvFilePath,
6933
+ "secret_scan",
6934
+ "Committed .env file in repository tree"
6935
+ )
5039
6936
  );
5040
- const hasEnvInHistory = committedEnvFilePath === void 0 && hasEnvPathInGitHistory(repoPath);
5041
- const dbLayerImportFile = migrationFiles.length === 0 ? implFiles.slice(0, 30).find((f) => fileContains(repoPath, f, DB_CLIENT_PATTERN)) : void 0;
5042
- const hasDataLayer = migrationFiles.length > 0 || dbLayerImportFile !== void 0;
5043
- const hasSecretsSurface = envExamples.length > 0 || committedEnvFilePath !== void 0 || gitignoreHasEnvRule || hasEnvInHistory;
5044
- if (!hasDataLayer && !hasSecretsSurface && !hasConfirmedSecretFinding && !cryptoHygiene.hasSurface) {
5045
- return buildNotApplicableSignal(
5046
- "A2",
5047
- "No data layer (DB/ORM/migrations) or ratable secrets surface detected \u2014 A2 not applicable to this repo archetype. A ratable surface requires .env* files, .gitignore .env rule, committed/history .env path, or detected signing/HMAC/secret-comparison code; bare env reads (process.env / os.environ / std::env::) do not qualify."
5048
- );
5049
- }
5050
- if (committedEnvFilePath) {
5051
- evidence.push(
5052
- evidenceForRelative(
5053
- repoPath,
5054
- committedEnvFilePath,
5055
- "secret_scan",
5056
- "Committed .env file in repository tree"
5057
- )
5058
- );
5059
- } else if (hasEnvInHistory) {
5060
- evidence.push({
5061
- kind: "secret_scan",
5062
- label: ".env path detected in git history",
5063
- path: ".git",
5064
- contentHash: readGitHead(repoPath) ?? "env-history-scan"
5065
- });
5066
- } else if (migrationFiles[0] != null) {
5067
- evidence.push(
5068
- evidenceForRelative(repoPath, migrationFiles[0], "artifact", "Data layer migration")
5069
- );
5070
- } else if (dbLayerImportFile != null) {
5071
- evidence.push(
5072
- evidenceForRelative(repoPath, dbLayerImportFile, "artifact", "DB client import")
5073
- );
5074
- }
6937
+ } else if (hasEnvInHistory) {
6938
+ evidence.push({
6939
+ kind: "secret_scan",
6940
+ label: ".env path detected in git history",
6941
+ path: ".git",
6942
+ contentHash: readGitHead(repoPath) ?? "env-history-scan"
6943
+ });
6944
+ } else if (migrationFiles[0] != null) {
6945
+ evidence.push(
6946
+ evidenceForRelative(repoPath, migrationFiles[0], "artifact", "Data layer migration")
6947
+ );
6948
+ } else if (dbLayerImportFile != null) {
6949
+ evidence.push(evidenceForRelative(repoPath, dbLayerImportFile, "artifact", "DB client import"));
5075
6950
  }
5076
6951
  const rlsMigration = migrationFiles.find((file) => fileContains(repoPath, file, RLS_PATTERN));
5077
6952
  const tenantScopedFile = migrationFiles.find(
@@ -5305,7 +7180,7 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles) {
5305
7180
  const evidence = [];
5306
7181
  const findings = [];
5307
7182
  const packageJson = findRootPackageJson(repoFiles);
5308
- const scripts = packageJson ? readPackageScripts(join(repoPath, packageJson)) : /* @__PURE__ */ new Map();
7183
+ const scripts = packageJson ? readPackageScripts(join3(repoPath, packageJson)) : /* @__PURE__ */ new Map();
5309
7184
  const workflows = repoFiles.filter(isCiWorkflow);
5310
7185
  const workflow = workflows[0];
5311
7186
  const deployConfigs = repoFiles.filter(isDeployConfig);
@@ -5579,45 +7454,6 @@ function hasDeclaredVersionConstraint(dependency) {
5579
7454
  return /^v?\d/.test(trimmed);
5580
7455
  }
5581
7456
  function collectA5ClaimRealityEvidence(repoPath, repoFiles) {
5582
- const alfredEvidence = [];
5583
- if (alfredEvidence.length > 0) {
5584
- const claimStats = readClaimRealityStats(repoPath);
5585
- return {
5586
- criterionId: "A5",
5587
- positiveEvidence: alfredEvidence,
5588
- findings: [],
5589
- metrics: [
5590
- metric(
5591
- "claim_match_rate",
5592
- "Claim match rate",
5593
- claimStats?.matchRate ?? 0,
5594
- 1,
5595
- 0.6,
5596
- "ratio",
5597
- "Measures supported and partially supported claims in the structured reconciliation artifact."
5598
- ),
5599
- metric(
5600
- "claim_registry_depth",
5601
- "Claim registry depth",
5602
- claimStats?.claimCount ?? 0,
5603
- Math.max(claimStats?.claimCount ?? 0, 8),
5604
- 0.25,
5605
- "claims",
5606
- "Credits the number of concrete claims reconciled without giving unlimited credit for one artifact."
5607
- ),
5608
- metric(
5609
- "reconciliation_artifact_depth",
5610
- "Reconciliation artifact depth",
5611
- alfredEvidence.length,
5612
- 3,
5613
- 0.15,
5614
- "artifacts",
5615
- "Credits implementation, Markdown, and structured reconciliation artifacts."
5616
- )
5617
- ],
5618
- notes: "A5 is evidenced by deterministic claim-vs-reality implementation and report artifacts, not a model-opinion verdict."
5619
- };
5620
- }
5621
7457
  const claimDoc = repoFiles.find((file) => /(^|\/)(README|readme)\.md$/.test(file)) ?? repoFiles.find((file) => /(^|\/)docs\/.*\.(md|mdx)$/.test(file));
5622
7458
  if (!claimDoc) {
5623
7459
  return buildNotApplicableSignal(
@@ -5778,7 +7614,7 @@ function collectB2PrTraceEvidence(repoPath, repoFiles) {
5778
7614
  function collectB3CiDisciplineEvidence(repoPath, repoFiles, mono) {
5779
7615
  const evidence = [];
5780
7616
  const packageJson = findRootPackageJson(repoFiles);
5781
- const scripts = packageJson ? readPackageScripts(join(repoPath, packageJson)) : /* @__PURE__ */ new Map();
7617
+ const scripts = packageJson ? readPackageScripts(join3(repoPath, packageJson)) : /* @__PURE__ */ new Map();
5782
7618
  const workflows = repoFiles.filter(isCiWorkflow);
5783
7619
  const workflow = workflows[0];
5784
7620
  const rootWorkflows = mono ? mono.sharedFiles.filter(isCiWorkflow) : [];
@@ -6193,13 +8029,13 @@ function listGitTrackedFiles(repoPath) {
6193
8029
  function visitRepoDir(repoPath, dirPath, files) {
6194
8030
  let entries;
6195
8031
  try {
6196
- entries = readdirSync(dirPath, { withFileTypes: true });
8032
+ entries = readdirSync3(dirPath, { withFileTypes: true });
6197
8033
  } catch {
6198
8034
  return;
6199
8035
  }
6200
8036
  for (const entry of entries) {
6201
8037
  if (shouldSkipDir(entry.name)) continue;
6202
- const fullPath = join(dirPath, entry.name);
8038
+ const fullPath = join3(dirPath, entry.name);
6203
8039
  if (entry.isDirectory()) {
6204
8040
  visitRepoDir(repoPath, fullPath, files);
6205
8041
  continue;
@@ -6207,7 +8043,7 @@ function visitRepoDir(repoPath, dirPath, files) {
6207
8043
  if (!entry.isFile()) continue;
6208
8044
  let size;
6209
8045
  try {
6210
- size = statSync(fullPath).size;
8046
+ size = statSync2(fullPath).size;
6211
8047
  } catch {
6212
8048
  continue;
6213
8049
  }
@@ -6243,9 +8079,9 @@ function isTestFile(file) {
6243
8079
  function collectContentBasedCppTestFiles(repoPath, repoFiles) {
6244
8080
  const candidates = repoFiles.filter((file) => /\.(cpp|cc|cxx)$/.test(file) && !isTestFile(file)).slice(0, CPP_CONTENT_SCAN_LIMIT);
6245
8081
  return candidates.filter((file) => {
6246
- const fullPath = join(repoPath, file);
8082
+ const fullPath = join3(repoPath, file);
6247
8083
  if (!isRegularFile(fullPath)) return false;
6248
- const contents = readFileSync(fullPath, "utf8");
8084
+ const contents = readFileSync6(fullPath, "utf8");
6249
8085
  return CPP_TEST_FRAMEWORK_PATTERN.test(contents) || CPP_TEST_MACRO_PATTERN.test(contents);
6250
8086
  });
6251
8087
  }
@@ -6264,9 +8100,9 @@ function measureNonHollowTestShare(repoPath, files) {
6264
8100
  let nonHollowCount = 0;
6265
8101
  let ratedCount = 0;
6266
8102
  for (const file of files) {
6267
- const fullPath = join(repoPath, file);
8103
+ const fullPath = join3(repoPath, file);
6268
8104
  if (!isRegularFile(fullPath)) continue;
6269
- const contents = readFileSync(fullPath, "utf8");
8105
+ const contents = readFileSync6(fullPath, "utf8");
6270
8106
  const nonHollow = (JS_TEST_ASSERTION_PATTERN.test(contents) || CPP_TEST_ASSERTION_PATTERN.test(contents) || MULTI_ECOSYSTEM_TEST_ASSERTION_PATTERN.test(contents)) && !SUITE_LEVEL_SKIP_PATTERN.test(contents);
6271
8107
  if (nonHollow) {
6272
8108
  nonHollowCount += 1;
@@ -6285,17 +8121,17 @@ function countPatternMatches(repoPath, files, pattern) {
6285
8121
  const matcher = new RegExp(pattern.source, flags);
6286
8122
  let count = 0;
6287
8123
  for (const file of files) {
6288
- const fullPath = join(repoPath, file);
8124
+ const fullPath = join3(repoPath, file);
6289
8125
  if (!isRegularFile(fullPath)) continue;
6290
- count += [...readFileSync(fullPath, "utf8").matchAll(matcher)].length;
8126
+ count += [...readFileSync6(fullPath, "utf8").matchAll(matcher)].length;
6291
8127
  }
6292
8128
  return count;
6293
8129
  }
6294
8130
  function readCoveragePercent(repoPath, files) {
6295
8131
  for (const file of files) {
6296
- const fullPath = join(repoPath, file);
8132
+ const fullPath = join3(repoPath, file);
6297
8133
  if (!isRegularFile(fullPath)) continue;
6298
- const contents = readFileSync(fullPath, "utf8");
8134
+ const contents = readFileSync6(fullPath, "utf8");
6299
8135
  const summaryPercent = parseCoverageSummaryPercent(contents);
6300
8136
  if (summaryPercent !== null) return summaryPercent;
6301
8137
  const thresholdPercent = parseCoverageThresholdPercent(contents);
@@ -6339,7 +8175,7 @@ function pickPrimaryDependencyManifest(repoPath, manifests) {
6339
8175
  function readDependencySpecs(repoPath, manifests) {
6340
8176
  const specs = [];
6341
8177
  for (const manifest of manifests) {
6342
- const fullPath = join(repoPath, manifest);
8178
+ const fullPath = join3(repoPath, manifest);
6343
8179
  if (!isRegularFile(fullPath)) continue;
6344
8180
  if (manifest.endsWith("package.json")) {
6345
8181
  specs.push(...readPackageJsonDependencySpecs(fullPath));
@@ -6385,7 +8221,7 @@ function readDependencySpecs(repoPath, manifests) {
6385
8221
  }
6386
8222
  function readGoModDependencySpecs(path) {
6387
8223
  const specs = [];
6388
- const contents = readFileSync(path, "utf8");
8224
+ const contents = readFileSync6(path, "utf8");
6389
8225
  const blockPattern = /require\s*\(([^)]+)\)/gs;
6390
8226
  const singlePattern = /^require\s+(\S+)\s+(\S+)/gm;
6391
8227
  for (const blockMatch of contents.matchAll(blockPattern)) {
@@ -6406,7 +8242,7 @@ function readGoModDependencySpecs(path) {
6406
8242
  }
6407
8243
  function readCargoTomlDependencySpecs(path) {
6408
8244
  const specs = [];
6409
- const contents = readFileSync(path, "utf8");
8245
+ const contents = readFileSync6(path, "utf8");
6410
8246
  const depSectionPattern = /^\[(?:dependencies|dev-dependencies|build-dependencies)\]([\s\S]*?)(?=^\[|(?![\s\S]))/gm;
6411
8247
  for (const sectionMatch of contents.matchAll(depSectionPattern)) {
6412
8248
  const section = sectionMatch[1] ?? "";
@@ -6428,7 +8264,7 @@ function readCargoTomlDependencySpecs(path) {
6428
8264
  }
6429
8265
  function readPyprojectDependencySpecs(path) {
6430
8266
  const specs = [];
6431
- const contents = readFileSync(path, "utf8");
8267
+ const contents = readFileSync6(path, "utf8");
6432
8268
  const poetrySectionPattern = /^\[tool\.poetry\.dependencies\]([\s\S]*?)(?=^\[|(?![\s\S]))/gm;
6433
8269
  for (const sectionMatch of contents.matchAll(poetrySectionPattern)) {
6434
8270
  const section = sectionMatch[1] ?? "";
@@ -6462,7 +8298,7 @@ function readPyprojectDependencySpecs(path) {
6462
8298
  }
6463
8299
  function readPipfileDependencySpecs(path) {
6464
8300
  const specs = [];
6465
- const contents = readFileSync(path, "utf8");
8301
+ const contents = readFileSync6(path, "utf8");
6466
8302
  const sectionPattern = /^\[(?:packages|dev-packages)\]([\s\S]*?)(?=^\[|$(?![\s\S]))/gm;
6467
8303
  for (const sectionMatch of contents.matchAll(sectionPattern)) {
6468
8304
  for (const line of (sectionMatch[1] ?? "").split("\n")) {
@@ -6475,7 +8311,7 @@ function readPipfileDependencySpecs(path) {
6475
8311
  return specs;
6476
8312
  }
6477
8313
  function readVcpkgJsonDependencySpecs(path) {
6478
- const parsed = parseJsonObject(readFileSync(path, "utf8"));
8314
+ const parsed = parseJsonObject(readFileSync6(path, "utf8"));
6479
8315
  if (!parsed) return [];
6480
8316
  const rawDeps = Array.isArray(parsed.dependencies) ? parsed.dependencies : [];
6481
8317
  const specs = [];
@@ -6491,7 +8327,7 @@ function readVcpkgJsonDependencySpecs(path) {
6491
8327
  }
6492
8328
  function readConanfileTxtDependencySpecs(path) {
6493
8329
  const specs = [];
6494
- const contents = readFileSync(path, "utf8");
8330
+ const contents = readFileSync6(path, "utf8");
6495
8331
  const requiresMatch = /^\[requires\]([\s\S]*?)(?=^\[|$(?![\s\S]))/m.exec(contents);
6496
8332
  if (!requiresMatch) return specs;
6497
8333
  for (const line of (requiresMatch[1] ?? "").split("\n")) {
@@ -6504,7 +8340,7 @@ function readConanfileTxtDependencySpecs(path) {
6504
8340
  }
6505
8341
  function readConanfilePyDependencySpecs(path) {
6506
8342
  const specs = [];
6507
- const contents = readFileSync(path, "utf8");
8343
+ const contents = readFileSync6(path, "utf8");
6508
8344
  for (const match of contents.matchAll(/self\.requires\s*\(\s*["']([\w.\-]+)\/([\w.\-]+)["']/g)) {
6509
8345
  specs.push({ name: match[1] ?? "", version: match[2] ?? "" });
6510
8346
  }
@@ -6518,7 +8354,7 @@ function readConanfilePyDependencySpecs(path) {
6518
8354
  }
6519
8355
  function readCMakeDependencySpecs(path) {
6520
8356
  const specs = [];
6521
- const contents = readFileSync(path, "utf8");
8357
+ const contents = readFileSync6(path, "utf8");
6522
8358
  for (const match of contents.matchAll(/find_package\s*\(\s*([\w]+)(?:\s+([\d.]+))?/gi)) {
6523
8359
  const name = match[1];
6524
8360
  if (!name) continue;
@@ -6533,7 +8369,7 @@ function readCMakeDependencySpecs(path) {
6533
8369
  return specs;
6534
8370
  }
6535
8371
  function readPackageJsonDependencySpecs(packageJsonPath) {
6536
- const parsed = parseJsonObject(readFileSync(packageJsonPath, "utf8"));
8372
+ const parsed = parseJsonObject(readFileSync6(packageJsonPath, "utf8"));
6537
8373
  if (!parsed) return [];
6538
8374
  const optionalPeerNames = /* @__PURE__ */ new Set();
6539
8375
  const peerDependenciesMeta = parsed.peerDependenciesMeta;
@@ -6560,7 +8396,7 @@ function readPackageJsonDependencySpecs(packageJsonPath) {
6560
8396
  return specs;
6561
8397
  }
6562
8398
  function readRequirementsDependencySpecs(path) {
6563
- return readFileSync(path, "utf8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map((line) => {
8399
+ return readFileSync6(path, "utf8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map((line) => {
6564
8400
  const [name = line, version = ""] = line.split(/[<>=~!]=?/, 2);
6565
8401
  return { name: name.trim(), version: line.includes("==") ? version.trim() : line };
6566
8402
  });
@@ -6575,33 +8411,10 @@ function isPinnedDependencyVersion(version) {
6575
8411
  if (trimmed === "find") return false;
6576
8412
  return /^\d+\.\d+(?:\.\d+)?(?:[-+][0-9A-Za-z.-]+)?$/.test(trimmed);
6577
8413
  }
6578
- function readClaimRealityStats(repoPath) {
6579
- const path = join(repoPath, "docs/witan/claim_reality_reconciliation.json");
6580
- if (!isRegularFile(path)) return null;
6581
- const parsed = parseJsonObject(readFileSync(path, "utf8"));
6582
- const claims = Array.isArray(parsed?.claims) ? parsed.claims : [];
6583
- if (claims.length === 0) return null;
6584
- const totalScore = claims.reduce((sum, claim) => {
6585
- if (!isRecord(claim) || typeof claim.status !== "string") return sum;
6586
- return sum + scoreClaimRealityStatus(claim.status);
6587
- }, 0);
6588
- return {
6589
- claimCount: claims.length,
6590
- matchRate: Math.round(totalScore / claims.length * 1e3) / 1e3
6591
- };
6592
- }
6593
- function scoreClaimRealityStatus(status) {
6594
- const normalized = status.toLowerCase();
6595
- if (/supported|verified|matched|true/.test(normalized) && !/unsupported/.test(normalized)) {
6596
- return 1;
6597
- }
6598
- if (/partial|mixed|needs[-_ ]?evidence/.test(normalized)) return 0.5;
6599
- return 0;
6600
- }
6601
8414
  function workflowTargetsDefaultBranch(repoPath, file) {
6602
- const fullPath = join(repoPath, file);
8415
+ const fullPath = join3(repoPath, file);
6603
8416
  if (!isRegularFile(fullPath)) return false;
6604
- const contents = readFileSync(fullPath, "utf8").toLowerCase();
8417
+ const contents = readFileSync6(fullPath, "utf8").toLowerCase();
6605
8418
  return /\bpull_request\b/.test(contents) || /\bbranches:\s*\[[^\]]*\b(main|master)\b[^\]]*\]/.test(contents) || /\bbranches:\s*\n\s*-\s*(main|master)\b/.test(contents);
6606
8419
  }
6607
8420
  function isPrMergeCommit(commit) {
@@ -6688,15 +8501,15 @@ function isDependencyUpdateConfig(file) {
6688
8501
  }
6689
8502
  function packageJsonHasAuditScript(repoPath, manifest) {
6690
8503
  if (!manifest.endsWith("package.json")) return false;
6691
- const scripts = readPackageScripts(join(repoPath, manifest));
8504
+ const scripts = readPackageScripts(join3(repoPath, manifest));
6692
8505
  return [...scripts.values()].some(
6693
8506
  (script) => /\b(audit|npm audit|pnpm audit|yarn audit)\b/i.test(script)
6694
8507
  );
6695
8508
  }
6696
8509
  function packageJsonHasHeavyTestDependency(repoPath, manifest) {
6697
- const fullPath = join(repoPath, manifest);
8510
+ const fullPath = join3(repoPath, manifest);
6698
8511
  if (!isRegularFile(fullPath)) return false;
6699
- const parsed = parseJsonObject(readFileSync(fullPath, "utf8"));
8512
+ const parsed = parseJsonObject(readFileSync6(fullPath, "utf8"));
6700
8513
  if (!parsed) return false;
6701
8514
  const names = [
6702
8515
  ...Object.keys(isRecord(parsed.dependencies) ? parsed.dependencies : {}),
@@ -6735,20 +8548,20 @@ function findRootPackageJson(repoFiles) {
6735
8548
  }
6736
8549
  function isRegularFile(path) {
6737
8550
  try {
6738
- return statSync(path).isFile();
8551
+ return statSync2(path).isFile();
6739
8552
  } catch {
6740
8553
  return false;
6741
8554
  }
6742
8555
  }
6743
8556
  function fileContains(repoPath, file, pattern) {
6744
- const fullPath = join(repoPath, file);
8557
+ const fullPath = join3(repoPath, file);
6745
8558
  if (!isRegularFile(fullPath)) return false;
6746
- return pattern.test(readFileSync(fullPath, "utf8"));
8559
+ return pattern.test(readFileSync6(fullPath, "utf8"));
6747
8560
  }
6748
8561
  function fileContainsCommittedSecret(repoPath, file) {
6749
- const fullPath = join(repoPath, file);
8562
+ const fullPath = join3(repoPath, file);
6750
8563
  if (!isRegularFile(fullPath)) return false;
6751
- return containsRealSecret(readFileSync(fullPath, "utf8"));
8564
+ return containsRealSecret(readFileSync6(fullPath, "utf8"));
6752
8565
  }
6753
8566
  function containsRealSecret(contents) {
6754
8567
  SECRET_ASSIGNMENT_PATTERN.lastIndex = 0;
@@ -6798,1019 +8611,742 @@ function collectHistorySecretEvidence(repoPath) {
6798
8611
  function readCredentialHistoryEntries(repoPath) {
6799
8612
  const entries = [];
6800
8613
  const seen = /* @__PURE__ */ new Set();
6801
- const appendEntry = (entry) => {
6802
- const key = `${entry.commit}:${entry.path}`;
6803
- if (seen.has(key)) return;
6804
- seen.add(key);
6805
- entries.push(entry);
6806
- };
6807
- try {
6808
- const commits = execFileSync("git", ["rev-list", "--all"], {
6809
- cwd: repoPath,
6810
- encoding: "utf8",
6811
- stdio: ["ignore", "pipe", "ignore"]
6812
- });
6813
- const output = execFileSync(
6814
- "git",
6815
- [
6816
- "diff-tree",
6817
- "--stdin",
6818
- "--root",
6819
- "--name-only",
6820
- "-r",
6821
- "--diff-filter=AM",
6822
- "--pretty=format:commit:%H"
6823
- ],
6824
- {
6825
- cwd: repoPath,
6826
- encoding: "utf8",
6827
- input: commits,
6828
- stdio: ["pipe", "pipe", "ignore"]
6829
- }
6830
- );
6831
- let currentCommit = null;
6832
- for (const line of output.split("\n")) {
6833
- if (line.startsWith("commit:")) {
6834
- currentCommit = line.slice("commit:".length);
6835
- continue;
6836
- }
6837
- const path = line.trim();
6838
- if (!currentCommit || !path) continue;
6839
- if (!isCredentialHistoryPath(path)) continue;
6840
- appendEntry({ commit: currentCommit, path });
6841
- }
6842
- } catch {
6843
- }
6844
- for (const entry of readDeletedCredentialHistoryEntries(repoPath)) {
6845
- appendEntry(entry);
6846
- }
6847
- return entries;
6848
- }
6849
- function readDeletedCredentialHistoryEntries(repoPath) {
6850
- const deletedPaths = readDeletedCredentialHistoryPaths(repoPath);
6851
- const entries = [];
6852
- const seen = /* @__PURE__ */ new Set();
6853
- for (const path of deletedPaths) {
6854
- for (const commit of readHistoryCommitsForPath(repoPath, path)) {
6855
- const key = `${commit}:${path}`;
6856
- if (seen.has(key)) continue;
6857
- seen.add(key);
6858
- entries.push({ commit, path });
6859
- }
6860
- }
6861
- return entries;
6862
- }
6863
- function readDeletedCredentialHistoryPaths(repoPath) {
6864
- try {
6865
- const output = execFileSync(
6866
- "git",
6867
- ["log", "--all", "--diff-filter=D", "--name-status", "--format=commit:%H"],
6868
- {
6869
- cwd: repoPath,
6870
- encoding: "utf8",
6871
- stdio: ["ignore", "pipe", "ignore"]
6872
- }
6873
- );
6874
- const paths = [];
6875
- const seen = /* @__PURE__ */ new Set();
6876
- for (const line of output.split("\n")) {
6877
- if (!line.startsWith("D ")) continue;
6878
- const path = line.slice(2).trim();
6879
- if (!path || seen.has(path)) continue;
6880
- if (!isCredentialHistoryPath(path)) continue;
6881
- seen.add(path);
6882
- paths.push(path);
6883
- }
6884
- return paths;
6885
- } catch {
6886
- return [];
6887
- }
6888
- }
6889
- function readHistoryCommitsForPath(repoPath, path) {
6890
- try {
6891
- const output = execFileSync(
6892
- "git",
6893
- ["log", "--all", "--diff-filter=AM", "--format=%H", "--", path],
6894
- {
6895
- cwd: repoPath,
6896
- encoding: "utf8",
6897
- stdio: ["ignore", "pipe", "ignore"]
6898
- }
6899
- );
6900
- return output.split("\n").map((line) => line.trim()).filter((line) => /^[0-9a-f]{40}$/i.test(line));
6901
- } catch {
6902
- return [];
6903
- }
6904
- }
6905
- function isCredentialHistoryPath(path) {
6906
- if (isHardExcludedPath(path)) return false;
6907
- const lowerPath = path.toLowerCase();
6908
- const basename3 = lowerPath.split("/").at(-1) ?? lowerPath;
6909
- if (/\.env(?:\.|$)/.test(basename3)) {
6910
- return true;
6911
- }
6912
- return lowerPath.includes("secret") || lowerPath.includes("credential") || basename3.endsWith(".pem") || basename3.endsWith(".key") || basename3 === "id_rsa" || basename3.endsWith(".p12") || basename3.endsWith(".pfx");
6913
- }
6914
- function isEnvHistoryPath(path) {
6915
- if (isEnvTemplatePath(path)) return false;
6916
- const basename3 = path.toLowerCase().split("/").at(-1) ?? path.toLowerCase();
6917
- return /\.env(?:\.|$)/.test(basename3);
6918
- }
6919
- function readGitBlob(repoPath, commit, path) {
6920
- try {
6921
- return execFileSync("git", ["show", `${commit}:${path}`], {
6922
- cwd: repoPath,
6923
- encoding: "utf8",
6924
- maxBuffer: 512e3,
6925
- stdio: ["ignore", "pipe", "ignore"]
6926
- });
6927
- } catch {
6928
- return null;
6929
- }
6930
- }
6931
- var ALL_CAPS_SNAKE_PLACEHOLDER_PATTERN = /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/;
6932
- function isPlaceholderSecretValue(value) {
6933
- const normalized = value.trim().toLowerCase();
6934
- return PLACEHOLDER_SECRET_PATTERN.test(normalized) || normalized.includes("your-") || normalized.includes("example") || ALL_CAPS_SNAKE_PLACEHOLDER_PATTERN.test(value.trim()) || isSyntheticFixtureSecretValue(normalized) || isFillerRunSecretValue(normalized);
6935
- }
6936
- function isFillerRunSecretValue(value) {
6937
- const candidates = [value];
6938
- const sepIdx = Math.max(value.lastIndexOf("_"), value.lastIndexOf("-"));
6939
- if (sepIdx > 0 && sepIdx < value.length - 1) {
6940
- candidates.push(value.slice(sepIdx + 1));
6941
- }
6942
- for (const body of candidates) {
6943
- if (body.length < 8) continue;
6944
- const counts = /* @__PURE__ */ new Map();
6945
- for (const ch of body) counts.set(ch, (counts.get(ch) ?? 0) + 1);
6946
- const distinct = counts.size;
6947
- const maxRun = Math.max(...counts.values());
6948
- if (distinct <= 2 && maxRun / body.length >= 0.8) return true;
6949
- }
6950
- return false;
6951
- }
6952
- function isSyntheticFixtureSecretValue(value) {
6953
- return value.includes("abcdefghijklmnopqrstuvwxyz") || value.includes("0123456789abcdefghijklmnopqrstuvwxyz") || value.includes("abcdefghijklmnopqrstuvwxyz0123456789") || value.includes("abcdefghijklmnopqrst");
6954
- }
6955
- function hasSuspiciousDependencies(repoPath, manifest) {
6956
- if (!manifest.endsWith("package.json")) return false;
6957
- const fullPath = join(repoPath, manifest);
6958
- if (!isRegularFile(fullPath)) return false;
6959
- const parsed = parseJsonObject(readFileSync(fullPath, "utf8"));
6960
- if (!parsed) return false;
6961
- const dependencies = isRecord(parsed.dependencies) ? parsed.dependencies : {};
6962
- const devDependencies = isRecord(parsed.devDependencies) ? parsed.devDependencies : {};
6963
- const names = [...Object.keys(dependencies), ...Object.keys(devDependencies)];
6964
- return names.some(
6965
- (name) => /^(todo|fake|placeholder|example)-|does-not-exist| hallucinated/i.test(name)
6966
- );
6967
- }
6968
- function readRecentCommits(repoPath) {
6969
- try {
6970
- const output = execFileSync("git", ["log", "--max-count=12", "--format=%H%x00%s"], {
6971
- cwd: repoPath,
6972
- encoding: "utf8",
6973
- stdio: ["ignore", "pipe", "ignore"]
6974
- }).trim();
6975
- if (!output) return [];
6976
- return output.split("\n").map((line) => {
6977
- const [sha = "", subject = ""] = line.split("\0");
6978
- return { sha, subject };
6979
- }).filter((commit) => commit.sha.length >= 7);
6980
- } catch {
6981
- return [];
6982
- }
6983
- }
6984
- function readPackageScripts(packageJsonPath) {
6985
- if (!isRegularFile(packageJsonPath)) return /* @__PURE__ */ new Map();
6986
- const parsed = parseJsonObject(readFileSync(packageJsonPath, "utf8"));
6987
- const scripts = parsed?.scripts;
6988
- if (!isRecord(scripts)) return /* @__PURE__ */ new Map();
6989
- return new Map(
6990
- Object.entries(scripts).filter(
6991
- (entry) => typeof entry[1] === "string"
6992
- )
6993
- );
6994
- }
6995
- function evidenceForRelative(repoPath, path, kind, label) {
6996
- const fullPath = join(repoPath, path);
6997
- const contents = isRegularFile(fullPath) ? readFileSync(fullPath, "utf8") : "";
6998
- return {
6999
- kind,
7000
- label,
7001
- path,
7002
- ...contents ? {
7003
- line: firstMeaningfulLine(contents),
7004
- contentHash: createHash("sha256").update(contents).digest("hex")
7005
- } : {}
7006
- };
7007
- }
7008
- function metric(name, label, value, max, weight, unit, description, kind) {
7009
- return {
7010
- name,
7011
- label,
7012
- value: Math.max(0, value),
7013
- max,
7014
- ...kind ? { kind } : {},
7015
- weight,
7016
- unit,
7017
- description
7018
- };
7019
- }
7020
- function firstMeaningfulLine(contents) {
7021
- const lines = contents.split("\n");
7022
- const index = lines.findIndex((line) => line.trim().length > 0);
7023
- return index === -1 ? 1 : index + 1;
7024
- }
7025
- function readGitHead(repoPath) {
8614
+ const appendEntry = (entry) => {
8615
+ const key = `${entry.commit}:${entry.path}`;
8616
+ if (seen.has(key)) return;
8617
+ seen.add(key);
8618
+ entries.push(entry);
8619
+ };
7026
8620
  try {
7027
- return execFileSync("git", ["rev-parse", "HEAD"], {
8621
+ const commits = execFileSync("git", ["rev-list", "--all"], {
7028
8622
  cwd: repoPath,
7029
8623
  encoding: "utf8",
7030
8624
  stdio: ["ignore", "pipe", "ignore"]
7031
- }).trim();
7032
- } catch {
7033
- return null;
7034
- }
7035
- }
7036
- function hasEnvPathInGitHistory(repoPath) {
7037
- try {
8625
+ });
7038
8626
  const output = execFileSync(
7039
8627
  "git",
7040
- ["log", "--all", "--diff-filter=AM", "--name-only", "--format="],
8628
+ [
8629
+ "diff-tree",
8630
+ "--stdin",
8631
+ "--root",
8632
+ "--name-only",
8633
+ "-r",
8634
+ "--diff-filter=AM",
8635
+ "--pretty=format:commit:%H"
8636
+ ],
7041
8637
  {
7042
8638
  cwd: repoPath,
7043
8639
  encoding: "utf8",
7044
- stdio: ["ignore", "pipe", "ignore"],
7045
- maxBuffer: 512e3
8640
+ input: commits,
8641
+ stdio: ["pipe", "pipe", "ignore"]
7046
8642
  }
7047
- ).trim();
7048
- if (!output) return false;
7049
- return output.split("\n").some((p) => /(?:^|\/)\.env(?:\.|$)/i.test(p.trim()));
8643
+ );
8644
+ let currentCommit = null;
8645
+ for (const line of output.split("\n")) {
8646
+ if (line.startsWith("commit:")) {
8647
+ currentCommit = line.slice("commit:".length);
8648
+ continue;
8649
+ }
8650
+ const path = line.trim();
8651
+ if (!currentCommit || !path) continue;
8652
+ if (!isCredentialHistoryPath(path)) continue;
8653
+ appendEntry({ commit: currentCommit, path });
8654
+ }
7050
8655
  } catch {
7051
- return false;
7052
8656
  }
7053
- }
7054
-
7055
- // src/witan/rubric.ts
7056
- var WITAN_RUBRIC = [
7057
- {
7058
- id: "A1",
7059
- title: "Test integrity and regression signal",
7060
- category: "code_trust"
7061
- },
7062
- {
7063
- id: "A2",
7064
- title: "Data-layer isolation and secrets posture",
7065
- category: "code_trust"
7066
- },
7067
- {
7068
- id: "A3",
7069
- title: "Production readiness",
7070
- category: "code_trust"
7071
- },
7072
- {
7073
- id: "A4",
7074
- title: "Dependency hygiene",
7075
- category: "code_trust"
7076
- },
7077
- {
7078
- id: "A5",
7079
- title: "Claim-vs-reality reconciliation",
7080
- category: "code_trust"
7081
- },
7082
- {
7083
- id: "B1",
7084
- title: "Dispatch trace completeness",
7085
- category: "process_trust"
7086
- },
7087
- {
7088
- id: "B2",
7089
- title: "PR outcome traceability",
7090
- category: "process_trust"
7091
- },
7092
- {
7093
- id: "B3",
7094
- title: "CI and QA discipline",
7095
- category: "process_trust"
7096
- },
7097
- {
7098
- id: "B4",
7099
- title: "Audit trail and report-up completeness",
7100
- category: "process_trust"
7101
- },
7102
- {
7103
- id: "B5",
7104
- title: "Verified learning trace",
7105
- category: "process_trust"
7106
- },
7107
- {
7108
- id: "B6",
7109
- title: "Privileged-operation human gating",
7110
- category: "process_trust"
8657
+ for (const entry of readDeletedCredentialHistoryEntries(repoPath)) {
8658
+ appendEntry(entry);
7111
8659
  }
7112
- ];
7113
-
7114
- // src/witan/scoring.ts
7115
- var MAX_SIGNAL_ADJUSTMENT = 0.8;
7116
- function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC) {
7117
- const parsedInput = WitanReportInputSchema.parse(input);
7118
- const signalsByCriterion = mergeSignalsByCriterion(parsedInput.signals);
7119
- const hasInputSignals = inputSignals != null && inputSignals.length > 0;
7120
- const inputSignalsByDimension = /* @__PURE__ */ new Map();
7121
- if (hasInputSignals && inputSignals) {
7122
- for (const signal of inputSignals) {
7123
- const bucket = inputSignalsByDimension.get(signal.dimension) ?? [];
7124
- bucket.push(signal);
7125
- inputSignalsByDimension.set(signal.dimension, bucket);
8660
+ return entries;
8661
+ }
8662
+ function readDeletedCredentialHistoryEntries(repoPath) {
8663
+ const deletedPaths = readDeletedCredentialHistoryPaths(repoPath);
8664
+ const entries = [];
8665
+ const seen = /* @__PURE__ */ new Set();
8666
+ for (const path of deletedPaths) {
8667
+ for (const commit of readHistoryCommitsForPath(repoPath, path)) {
8668
+ const key = `${commit}:${path}`;
8669
+ if (seen.has(key)) continue;
8670
+ seen.add(key);
8671
+ entries.push({ commit, path });
7126
8672
  }
7127
8673
  }
7128
- const consumedSignals = [];
7129
- const criteria = rubric.map((rubricCriterion) => {
7130
- const id = rubricCriterion.id;
7131
- const signal = signalsByCriterion.get(id);
7132
- const scored = scoreCriterion(signal, parsedInput.rubricVersion);
7133
- const dimensionSignals = inputSignalsByDimension.get(id);
7134
- if (!dimensionSignals || dimensionSignals.length === 0) {
7135
- return {
7136
- id,
7137
- title: rubricCriterion.title,
7138
- category: rubricCriterion.category,
7139
- score: scored.score,
7140
- status: scored.status,
7141
- evidence: scored.evidence,
7142
- findings: scored.findings,
7143
- metrics: scored.metrics,
7144
- ...signal?.notes ? { notes: signal.notes } : {}
7145
- };
7146
- }
7147
- const { adjustedScore, adjustment, summaries } = applySignalAdjustment(
7148
- scored.score,
7149
- id,
7150
- dimensionSignals
8674
+ return entries;
8675
+ }
8676
+ function readDeletedCredentialHistoryPaths(repoPath) {
8677
+ try {
8678
+ const output = execFileSync(
8679
+ "git",
8680
+ ["log", "--all", "--diff-filter=D", "--name-status", "--format=commit:%H"],
8681
+ {
8682
+ cwd: repoPath,
8683
+ encoding: "utf8",
8684
+ stdio: ["ignore", "pipe", "ignore"]
8685
+ }
7151
8686
  );
7152
- consumedSignals.push(...summaries);
7153
- const finalScore = adjustment === 0 ? scored.score : adjustedScore;
7154
- const finalStatus = adjustment === 0 ? scored.status : statusForScore(finalScore);
7155
- return {
7156
- id,
7157
- title: rubricCriterion.title,
7158
- category: rubricCriterion.category,
7159
- score: finalScore,
7160
- ...adjustment !== 0 ? { nativeScore: scored.score } : {},
7161
- status: finalStatus,
7162
- evidence: scored.evidence,
7163
- findings: scored.findings,
7164
- metrics: scored.metrics,
7165
- ...signal?.notes ? { notes: signal.notes } : {}
7166
- };
7167
- });
7168
- const categoryOrder = [];
7169
- for (const rubricCriterion of rubric) {
7170
- if (!categoryOrder.includes(rubricCriterion.category)) {
7171
- categoryOrder.push(rubricCriterion.category);
8687
+ const paths = [];
8688
+ const seen = /* @__PURE__ */ new Set();
8689
+ for (const line of output.split("\n")) {
8690
+ if (!line.startsWith("D ")) continue;
8691
+ const path = line.slice(2).trim();
8692
+ if (!path || seen.has(path)) continue;
8693
+ if (!isCredentialHistoryPath(path)) continue;
8694
+ seen.add(path);
8695
+ paths.push(path);
7172
8696
  }
8697
+ return paths;
8698
+ } catch {
8699
+ return [];
7173
8700
  }
7174
- const categoryScoreMap = {};
7175
- for (const category of categoryOrder) {
7176
- categoryScoreMap[category] = averageScore(
7177
- criteria.filter((criterion) => criterion.category === category)
7178
- );
7179
- }
7180
- const firstCategory = categoryOrder[0];
7181
- const secondCategory = categoryOrder[1] ?? firstCategory;
7182
- const codeTrustScore = categoryScoreMap.code_trust ?? (firstCategory ? categoryScoreMap[firstCategory] : void 0) ?? 0;
7183
- const processTrustScore = categoryScoreMap.process_trust ?? (secondCategory ? categoryScoreMap[secondCategory] : void 0) ?? 0;
7184
- const overallScore = roundScore(
7185
- categoryOrder.reduce((sum, category) => sum + (categoryScoreMap[category] ?? 0), 0) / Math.max(categoryOrder.length, 1)
7186
- );
7187
- return WitanReportSchema.parse({
7188
- productSlug: parsedInput.productSlug,
7189
- productDisplayName: parsedInput.productDisplayName,
7190
- repo: parsedInput.repo,
7191
- generatedAt: parsedInput.generatedAt,
7192
- rubricVersion: parsedInput.rubricVersion,
7193
- codeTrustScore,
7194
- processTrustScore,
7195
- overallScore,
7196
- criteria,
7197
- ...consumedSignals.length > 0 ? { consumedSignals } : {},
7198
- // Only surface the full per-category map for rubrics with more than two buckets —
7199
- // the default two-category rubric is fully represented by codeTrustScore/processTrustScore.
7200
- ...categoryOrder.length > 2 ? { categoryScores: categoryScoreMap } : {},
7201
- // Pass through repo-archetype metadata verbatim — createWitanReport does not compute or
7202
- // alter it (see classifyRepoArchetype in repo-signals.ts); per-criterion scoring above is
7203
- // unaffected by archetype, only presentation layers (badge/terminal/verdict) key off it.
7204
- ...parsedInput.archetype ? { archetype: parsedInput.archetype } : {},
7205
- ...parsedInput.insufficientSourceReason ? { insufficientSourceReason: parsedInput.insufficientSourceReason } : {}
7206
- });
7207
8701
  }
7208
- function applySignalAdjustment(nativeScore, dimension, signals) {
7209
- const summaries = [];
7210
- let totalRawAdj = 0;
7211
- for (const signal of signals) {
7212
- const criticalCount = signal.findings.filter((f) => f.severity === "critical").length;
7213
- const warningCount = signal.findings.filter((f) => f.severity === "warning").length;
7214
- const infoCount = signal.findings.filter((f) => f.severity === "info").length;
7215
- const severityScore = Math.min(
7216
- criticalCount * 0.2 + warningCount * 0.1 + infoCount * 0.02,
7217
- 1
7218
- );
7219
- const signalAdj = signal.weight * severityScore * MAX_SIGNAL_ADJUSTMENT;
7220
- totalRawAdj += signalAdj;
7221
- summaries.push({
7222
- source: signal.source,
7223
- dimension,
7224
- findingCount: signal.findings.length,
7225
- severityBreakdown: { critical: criticalCount, warning: warningCount, info: infoCount },
7226
- nativeScore,
7227
- scoreAdjustment: -roundScore(Math.min(signalAdj, MAX_SIGNAL_ADJUSTMENT)),
7228
- adjustedScore: roundScore(
7229
- Math.max(0, nativeScore - Math.min(signalAdj, MAX_SIGNAL_ADJUSTMENT))
7230
- ),
7231
- findings: [...signal.findings]
7232
- });
8702
+ function readHistoryCommitsForPath(repoPath, path) {
8703
+ try {
8704
+ const output = execFileSync(
8705
+ "git",
8706
+ ["log", "--all", "--diff-filter=AM", "--format=%H", "--", path],
8707
+ {
8708
+ cwd: repoPath,
8709
+ encoding: "utf8",
8710
+ stdio: ["ignore", "pipe", "ignore"]
8711
+ }
8712
+ );
8713
+ return output.split("\n").map((line) => line.trim()).filter((line) => /^[0-9a-f]{40}$/i.test(line));
8714
+ } catch {
8715
+ return [];
7233
8716
  }
7234
- const totalAdj = Math.min(totalRawAdj, MAX_SIGNAL_ADJUSTMENT);
7235
- const adjustedScore = roundScore(Math.max(0, nativeScore - totalAdj));
7236
- for (const summary of summaries) {
7237
- summary.adjustedScore = adjustedScore;
7238
- summary.scoreAdjustment = -roundScore(totalAdj);
8717
+ }
8718
+ function isCredentialHistoryPath(path) {
8719
+ if (isHardExcludedPath(path)) return false;
8720
+ const lowerPath = path.toLowerCase();
8721
+ const basename4 = lowerPath.split("/").at(-1) ?? lowerPath;
8722
+ if (/\.env(?:\.|$)/.test(basename4)) {
8723
+ return true;
7239
8724
  }
7240
- return { adjustedScore, adjustment: roundScore(totalAdj), summaries };
8725
+ return lowerPath.includes("secret") || lowerPath.includes("credential") || basename4.endsWith(".pem") || basename4.endsWith(".key") || basename4 === "id_rsa" || basename4.endsWith(".p12") || basename4.endsWith(".pfx");
7241
8726
  }
7242
- function statusForScore(score) {
7243
- if (score >= 3.5) return "verified";
7244
- if (score >= 2.5) return "info";
7245
- if (score >= 1.5) return "warning";
7246
- if (score > 0) return "critical";
7247
- return "critical";
8727
+ function isEnvHistoryPath(path) {
8728
+ if (isEnvTemplatePath(path)) return false;
8729
+ const basename4 = path.toLowerCase().split("/").at(-1) ?? path.toLowerCase();
8730
+ return /\.env(?:\.|$)/.test(basename4);
7248
8731
  }
7249
- function mergeSignalsByCriterion(signals) {
7250
- const merged = /* @__PURE__ */ new Map();
7251
- for (const signal of signals) {
7252
- const existing = merged.get(signal.criterionId);
7253
- if (!existing) {
7254
- merged.set(signal.criterionId, signal);
7255
- continue;
7256
- }
7257
- merged.set(signal.criterionId, {
7258
- criterionId: signal.criterionId,
7259
- positiveEvidence: [...existing.positiveEvidence, ...signal.positiveEvidence],
7260
- findings: [...existing.findings, ...signal.findings],
7261
- metrics: [...existing.metrics ?? [], ...signal.metrics ?? []],
7262
- notes: signal.notes ?? existing.notes,
7263
- // N/A wins on merge: if either signal is N/A, the criterion is N/A.
7264
- ...existing.notApplicable === true || signal.notApplicable === true ? { notApplicable: true } : {}
8732
+ function readGitBlob(repoPath, commit, path) {
8733
+ try {
8734
+ return execFileSync("git", ["show", `${commit}:${path}`], {
8735
+ cwd: repoPath,
8736
+ encoding: "utf8",
8737
+ maxBuffer: 512e3,
8738
+ stdio: ["ignore", "pipe", "ignore"]
7265
8739
  });
8740
+ } catch {
8741
+ return null;
7266
8742
  }
7267
- return merged;
7268
- }
7269
- function usesMetricScoring(rubricVersion) {
7270
- return rubricVersion === WITAN_RUBRIC_VERSION_V1 || rubricVersion === WITAN_RUBRIC_VERSION_V2 || rubricVersion === WITAN_TRADING_RUBRIC_VERSION_V0;
7271
8743
  }
7272
- function unmeasuredStatus(rubricVersion) {
7273
- return rubricVersion === WITAN_TRADING_RUBRIC_VERSION_V0 ? "unverified" : "insufficient_data";
8744
+ var ALL_CAPS_SNAKE_PLACEHOLDER_PATTERN = /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/;
8745
+ function isPlaceholderSecretValue(value) {
8746
+ const normalized = value.trim().toLowerCase();
8747
+ return PLACEHOLDER_SECRET_PATTERN.test(normalized) || normalized.includes("your-") || normalized.includes("example") || ALL_CAPS_SNAKE_PLACEHOLDER_PATTERN.test(value.trim()) || isSyntheticFixtureSecretValue(normalized) || isFillerRunSecretValue(normalized);
7274
8748
  }
7275
- function scoreCriterion(signal, rubricVersion) {
7276
- if (!signal) {
7277
- return {
7278
- score: 0,
7279
- status: unmeasuredStatus(rubricVersion),
7280
- evidence: [],
7281
- findings: [],
7282
- metrics: []
7283
- };
7284
- }
7285
- if (signal.notApplicable === true) {
7286
- return {
7287
- score: 0,
7288
- status: "not_applicable",
7289
- evidence: [],
7290
- findings: [],
7291
- metrics: []
7292
- };
7293
- }
7294
- const evidence = signal.positiveEvidence;
7295
- const findings = signal.findings;
7296
- const metrics = signal.metrics ?? [];
7297
- const evidenceCount = evidence.length + findings.length;
7298
- if (evidenceCount === 0) {
7299
- if (!usesMetricScoring(rubricVersion) || metrics.length === 0) {
7300
- return {
7301
- score: 0,
7302
- status: unmeasuredStatus(rubricVersion),
7303
- evidence,
7304
- findings,
7305
- metrics
7306
- };
7307
- }
7308
- }
7309
- if (usesMetricScoring(rubricVersion)) {
7310
- const measuredMetrics = metrics.length > 0 ? metrics : fallbackMetricsForEvidence(evidence, findings);
7311
- const score = capScoreForFindings(scoreMetrics(measuredMetrics), findings);
7312
- const status = statusForScoreAndFindings(score, findings);
7313
- return {
7314
- score,
7315
- status,
7316
- evidence,
7317
- findings: ensureFindingsExplainStatus(signal.criterionId, score, status, findings, evidence),
7318
- metrics: measuredMetrics
7319
- };
7320
- }
7321
- if (findings.some((finding) => finding.severity === "critical")) {
7322
- return {
7323
- score: 1,
7324
- status: "critical",
7325
- evidence,
7326
- findings,
7327
- metrics
7328
- };
7329
- }
7330
- if (findings.some((finding) => finding.severity === "warning")) {
7331
- return {
7332
- score: 2,
7333
- status: "warning",
7334
- evidence,
7335
- findings,
7336
- metrics
7337
- };
8749
+ function isFillerRunSecretValue(value) {
8750
+ const candidates = [value];
8751
+ const sepIdx = Math.max(value.lastIndexOf("_"), value.lastIndexOf("-"));
8752
+ if (sepIdx > 0 && sepIdx < value.length - 1) {
8753
+ candidates.push(value.slice(sepIdx + 1));
7338
8754
  }
7339
- if (findings.some((finding) => finding.severity === "info")) {
7340
- return {
7341
- score: 3,
7342
- status: "info",
7343
- evidence,
7344
- findings,
7345
- metrics
7346
- };
8755
+ for (const body of candidates) {
8756
+ if (body.length < 8) continue;
8757
+ const counts = /* @__PURE__ */ new Map();
8758
+ for (const ch of body) counts.set(ch, (counts.get(ch) ?? 0) + 1);
8759
+ const distinct = counts.size;
8760
+ const maxRun = Math.max(...counts.values());
8761
+ if (distinct <= 2 && maxRun / body.length >= 0.8) return true;
7347
8762
  }
7348
- return {
7349
- score: 4,
7350
- status: "verified",
7351
- evidence,
7352
- findings,
7353
- metrics
7354
- };
8763
+ return false;
7355
8764
  }
7356
- function fallbackMetricsForEvidence(evidence, findings) {
7357
- return [
7358
- {
7359
- name: "evidence_depth",
7360
- label: "Evidence depth",
7361
- value: Math.min(evidence.length + findings.length + 1, 4),
7362
- max: 4,
7363
- weight: 1,
7364
- unit: "signals",
7365
- description: "Conservative v1 fallback for externally supplied evidence that has not provided criterion-specific depth metrics."
7366
- }
7367
- ];
8765
+ function isSyntheticFixtureSecretValue(value) {
8766
+ return value.includes("abcdefghijklmnopqrstuvwxyz") || value.includes("0123456789abcdefghijklmnopqrstuvwxyz") || value.includes("abcdefghijklmnopqrstuvwxyz0123456789") || value.includes("abcdefghijklmnopqrst");
7368
8767
  }
7369
- function scoreMetrics(metrics) {
7370
- let weightedTotal = 0;
7371
- let totalWeight = 0;
7372
- for (const metric2 of metrics) {
7373
- const normalized = metric2.max ? Math.min(metric2.value / metric2.max, 1) : Math.min(metric2.value, 1);
7374
- weightedTotal += normalized * metric2.weight;
7375
- totalWeight += metric2.weight;
7376
- }
7377
- if (totalWeight === 0) return 0;
7378
- return roundScore(weightedTotal / totalWeight * 4);
8768
+ function hasSuspiciousDependencies(repoPath, manifest) {
8769
+ if (!manifest.endsWith("package.json")) return false;
8770
+ const fullPath = join3(repoPath, manifest);
8771
+ if (!isRegularFile(fullPath)) return false;
8772
+ const parsed = parseJsonObject(readFileSync6(fullPath, "utf8"));
8773
+ if (!parsed) return false;
8774
+ const dependencies = isRecord(parsed.dependencies) ? parsed.dependencies : {};
8775
+ const devDependencies = isRecord(parsed.devDependencies) ? parsed.devDependencies : {};
8776
+ const names = [...Object.keys(dependencies), ...Object.keys(devDependencies)];
8777
+ return names.some(
8778
+ (name) => /^(todo|fake|placeholder|example)-|does-not-exist| hallucinated/i.test(name)
8779
+ );
7379
8780
  }
7380
- function capScoreForFindings(score, findings) {
7381
- if (findings.some((finding) => finding.severity === "critical")) return 1.4;
7382
- if (findings.some((finding) => finding.severity === "warning")) return Math.min(score, 2.4);
7383
- if (findings.some((finding) => finding.severity === "info")) return Math.min(score, 3.4);
7384
- return score;
8781
+ function readRecentCommits(repoPath) {
8782
+ try {
8783
+ const output = execFileSync("git", ["log", "--max-count=12", "--format=%H%x00%s"], {
8784
+ cwd: repoPath,
8785
+ encoding: "utf8",
8786
+ stdio: ["ignore", "pipe", "ignore"]
8787
+ }).trim();
8788
+ if (!output) return [];
8789
+ return output.split("\n").map((line) => {
8790
+ const [sha = "", subject = ""] = line.split("\0");
8791
+ return { sha, subject };
8792
+ }).filter((commit) => commit.sha.length >= 7);
8793
+ } catch {
8794
+ return [];
8795
+ }
7385
8796
  }
7386
- function statusForScoreAndFindings(score, findings) {
7387
- if (findings.some((finding) => finding.severity === "critical")) return "critical";
7388
- if (findings.some((finding) => finding.severity === "warning")) return "warning";
7389
- if (score >= 3.5 && findings.length === 0) return "verified";
7390
- if (score >= 2.5) return "info";
7391
- if (score >= 1.5) return "warning";
7392
- return "critical";
8797
+ function readPackageScripts(packageJsonPath) {
8798
+ if (!isRegularFile(packageJsonPath)) return /* @__PURE__ */ new Map();
8799
+ const parsed = parseJsonObject(readFileSync6(packageJsonPath, "utf8"));
8800
+ const scripts = parsed?.scripts;
8801
+ if (!isRecord(scripts)) return /* @__PURE__ */ new Map();
8802
+ return new Map(
8803
+ Object.entries(scripts).filter(
8804
+ (entry) => typeof entry[1] === "string"
8805
+ )
8806
+ );
7393
8807
  }
7394
- function ensureFindingsExplainStatus(criterionId, score, status, findings, evidence) {
7395
- if (findings.length > 0) return [...findings];
7396
- if (status !== "critical" && status !== "warning") return [...findings];
7397
- const anchor = evidence[0] ?? {
7398
- kind: "repository",
7399
- label: `${criterionId} metric-derived score`,
7400
- path: "."
8808
+ function evidenceForRelative(repoPath, path, kind, label) {
8809
+ const fullPath = join3(repoPath, path);
8810
+ const contents = isRegularFile(fullPath) ? readFileSync6(fullPath, "utf8") : "";
8811
+ return {
8812
+ kind,
8813
+ label,
8814
+ path,
8815
+ ...contents ? {
8816
+ line: firstMeaningfulLine(contents),
8817
+ contentHash: createHash("sha256").update(contents).digest("hex")
8818
+ } : {}
8819
+ };
8820
+ }
8821
+ function metric(name, label, value, max, weight, unit, description, kind) {
8822
+ return {
8823
+ name,
8824
+ label,
8825
+ value: Math.max(0, value),
8826
+ max,
8827
+ ...kind ? { kind } : {},
8828
+ weight,
8829
+ unit,
8830
+ description
7401
8831
  };
7402
- return [
7403
- {
7404
- severity: status,
7405
- summary: `${criterionId} metric-derived score is ${score.toFixed(1)}/4.0, in the ${status} band \u2014 no single finding drove this; it reflects the combined metric weighting below.`,
7406
- evidence: anchor
7407
- }
7408
- ];
7409
8832
  }
7410
- function averageScore(criteria) {
7411
- const applicable = criteria.filter(
7412
- (criterion) => criterion.status !== "not_applicable" && criterion.status !== "insufficient_data"
7413
- );
7414
- if (applicable.length === 0) return 0;
7415
- const total = applicable.reduce((sum, criterion) => sum + criterion.score, 0);
7416
- return roundScore(total / applicable.length);
8833
+ function firstMeaningfulLine(contents) {
8834
+ const lines = contents.split("\n");
8835
+ const index = lines.findIndex((line) => line.trim().length > 0);
8836
+ return index === -1 ? 1 : index + 1;
7417
8837
  }
7418
- function roundScore(score) {
7419
- return Math.round(score * 10) / 10;
8838
+ function readGitHead(repoPath) {
8839
+ try {
8840
+ return execFileSync("git", ["rev-parse", "HEAD"], {
8841
+ cwd: repoPath,
8842
+ encoding: "utf8",
8843
+ stdio: ["ignore", "pipe", "ignore"]
8844
+ }).trim();
8845
+ } catch {
8846
+ return null;
8847
+ }
8848
+ }
8849
+ function hasEnvPathInGitHistory(repoPath) {
8850
+ try {
8851
+ const output = execFileSync(
8852
+ "git",
8853
+ ["log", "--all", "--diff-filter=AM", "--name-only", "--format="],
8854
+ {
8855
+ cwd: repoPath,
8856
+ encoding: "utf8",
8857
+ stdio: ["ignore", "pipe", "ignore"],
8858
+ maxBuffer: 512e3
8859
+ }
8860
+ ).trim();
8861
+ if (!output) return false;
8862
+ return output.split("\n").some((p) => /(?:^|\/)\.env(?:\.|$)/i.test(p.trim()));
8863
+ } catch {
8864
+ return false;
8865
+ }
7420
8866
  }
7421
8867
 
7422
- // src/witan/sarif-adapter.ts
7423
- import { readFileSync as readFileSync2 } from "fs";
7424
- var DIMENSION_RULES = [
7425
- // A2 — injection / secrets / auth (high weight)
7426
- {
7427
- pattern: /sql[- _]?inject|cwe-89|sqli|command[- _]?inject|cwe-78|os[- _]?cmd|rce|remote[- _]?code/i,
7428
- dimension: "A2",
7429
- weight: 0.9
7430
- },
7431
- {
7432
- pattern: /secret|credential|api[- _]?key|password[- _]?leak|hardcoded[- _]?(token|key|secret)|cwe-259|cwe-798/i,
7433
- dimension: "A2",
7434
- weight: 0.9
7435
- },
8868
+ // ../witan/src/rubric.ts
8869
+ var WITAN_RUBRIC = [
7436
8870
  {
7437
- pattern: /path[- _]?traversal|directory[- _]?traversal|cwe-22|lfi|rfi/i,
7438
- dimension: "A2",
7439
- weight: 0.8
8871
+ id: "A1",
8872
+ title: "Test integrity and regression signal",
8873
+ category: "code_trust"
7440
8874
  },
7441
8875
  {
7442
- pattern: /ssrf|server[- _]?side[- _]?request[- _]?forg|cwe-918/i,
7443
- dimension: "A2",
7444
- weight: 0.8
8876
+ id: "A2",
8877
+ title: "Data-layer isolation and secrets posture",
8878
+ category: "code_trust"
7445
8879
  },
7446
8880
  {
7447
- pattern: /xxe|xml[- _]?external[- _]?entity|cwe-611/i,
7448
- dimension: "A2",
7449
- weight: 0.8
8881
+ id: "A3",
8882
+ title: "Production readiness",
8883
+ category: "code_trust"
7450
8884
  },
7451
8885
  {
7452
- pattern: /auth[- _]?bypass|broken[- _]?auth|missing[- _]?auth|cwe-287|cwe-306|cwe-863|cwe-862/i,
7453
- dimension: "A2",
7454
- weight: 0.7
8886
+ id: "A4",
8887
+ title: "Dependency hygiene",
8888
+ category: "code_trust"
7455
8889
  },
7456
8890
  {
7457
- pattern: /xss|cross[- _]?site[- _]?script|cwe-79/i,
7458
- dimension: "A2",
7459
- weight: 0.6
8891
+ id: "A5",
8892
+ title: "Claim-vs-reality reconciliation",
8893
+ category: "code_trust"
7460
8894
  },
7461
- // A4 — dependency vulnerabilities
7462
8895
  {
7463
- pattern: /cve-\d{4}-\d+|supply[- _]?chain|depend[a-z]*[- _]?vulner|outdated[- _]?depend|deprecated[- _]?package/i,
7464
- dimension: "A4",
7465
- weight: 0.8
8896
+ id: "B1",
8897
+ title: "Dispatch trace completeness",
8898
+ category: "process_trust"
7466
8899
  },
7467
8900
  {
7468
- pattern: /npm[- _]?audit|yarn[- _]?audit|pnpm[- _]?audit|pip[- _]?audit|cargo[- _]?audit/i,
7469
- dimension: "A4",
7470
- weight: 0.7
8901
+ id: "B2",
8902
+ title: "PR outcome traceability",
8903
+ category: "process_trust"
7471
8904
  },
7472
- // A3 — production readiness / insecure config
7473
8905
  {
7474
- pattern: /insecure[- _]?tls|weak[- _]?cipher|tls[- _]?version|http[- _]?strict|hsts|csp[- _]?missing|cwe-326|cwe-327/i,
7475
- dimension: "A3",
7476
- weight: 0.5
8906
+ id: "B3",
8907
+ title: "CI and QA discipline",
8908
+ category: "process_trust"
7477
8909
  },
7478
8910
  {
7479
- pattern: /error[- _]?handl|unhandled[- _]?exception|crash|uncaught|stacktrace[- _]?leak/i,
7480
- dimension: "A3",
7481
- weight: 0.4
8911
+ id: "B4",
8912
+ title: "Audit trail and report-up completeness",
8913
+ category: "process_trust"
7482
8914
  },
7483
8915
  {
7484
- pattern: /debug[- _]?mode|dev[- _]?endpoint|staging[- _]?secret/i,
7485
- dimension: "A3",
7486
- weight: 0.4
8916
+ id: "B5",
8917
+ title: "Verified learning trace",
8918
+ category: "process_trust"
7487
8919
  },
7488
- // A1 — test integrity (low weight; SARIF rarely surfaces this)
7489
8920
  {
7490
- pattern: /skipped[- _]?test|hollow[- _]?test|empty[- _]?assert|no[- _]?assertion/i,
7491
- dimension: "A1",
7492
- weight: 0.2
8921
+ id: "B6",
8922
+ title: "Privileged-operation human gating",
8923
+ category: "process_trust"
7493
8924
  }
7494
8925
  ];
7495
- var DEFAULT_DIMENSION = "A2";
7496
- var DEFAULT_WEIGHT = 0.3;
7497
- var DROP_LEVEL = /* @__PURE__ */ new Set(["none"]);
7498
- function mapLevel(level) {
7499
- if (!level || DROP_LEVEL.has(level)) return null;
7500
- if (level === "error") return "critical";
7501
- if (level === "warning") return "warning";
7502
- return "info";
7503
- }
7504
- function mapRuleToDimension(ruleId, rules) {
7505
- for (const rule of rules) {
7506
- if (rule.pattern.test(ruleId)) {
7507
- return { dimension: rule.dimension, weight: rule.weight };
8926
+
8927
+ // ../witan/src/scoring.ts
8928
+ var MAX_SIGNAL_ADJUSTMENT = 0.8;
8929
+ function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC) {
8930
+ const parsedInput = WitanReportInputSchema.parse(input);
8931
+ const signalsByCriterion = mergeSignalsByCriterion(parsedInput.signals);
8932
+ const hasInputSignals = inputSignals != null && inputSignals.length > 0;
8933
+ const inputSignalsByDimension = /* @__PURE__ */ new Map();
8934
+ if (hasInputSignals && inputSignals) {
8935
+ for (const signal of inputSignals) {
8936
+ const bucket = inputSignalsByDimension.get(signal.dimension) ?? [];
8937
+ bucket.push(signal);
8938
+ inputSignalsByDimension.set(signal.dimension, bucket);
8939
+ }
8940
+ }
8941
+ const consumedSignals = [];
8942
+ const criteria = rubric.map((rubricCriterion) => {
8943
+ const id = rubricCriterion.id;
8944
+ const signal = signalsByCriterion.get(id);
8945
+ const scored = scoreCriterion(signal, parsedInput.rubricVersion);
8946
+ const dimensionSignals = inputSignalsByDimension.get(id);
8947
+ if (!dimensionSignals || dimensionSignals.length === 0) {
8948
+ return {
8949
+ id,
8950
+ title: rubricCriterion.title,
8951
+ category: rubricCriterion.category,
8952
+ score: scored.score,
8953
+ status: scored.status,
8954
+ evidence: scored.evidence,
8955
+ findings: scored.findings,
8956
+ metrics: scored.metrics,
8957
+ ...signal?.notes ? { notes: signal.notes } : {}
8958
+ };
8959
+ }
8960
+ const { adjustedScore, adjustment, summaries } = applySignalAdjustment(
8961
+ scored.score,
8962
+ id,
8963
+ dimensionSignals
8964
+ );
8965
+ consumedSignals.push(...summaries);
8966
+ const finalScore = adjustment === 0 ? scored.score : adjustedScore;
8967
+ const finalStatus = adjustment === 0 ? scored.status : statusForScore(finalScore);
8968
+ return {
8969
+ id,
8970
+ title: rubricCriterion.title,
8971
+ category: rubricCriterion.category,
8972
+ score: finalScore,
8973
+ ...adjustment !== 0 ? { nativeScore: scored.score } : {},
8974
+ status: finalStatus,
8975
+ evidence: scored.evidence,
8976
+ findings: scored.findings,
8977
+ metrics: scored.metrics,
8978
+ ...signal?.notes ? { notes: signal.notes } : {}
8979
+ };
8980
+ });
8981
+ const categoryOrder = [];
8982
+ for (const rubricCriterion of rubric) {
8983
+ if (!categoryOrder.includes(rubricCriterion.category)) {
8984
+ categoryOrder.push(rubricCriterion.category);
7508
8985
  }
7509
8986
  }
7510
- return { dimension: DEFAULT_DIMENSION, weight: DEFAULT_WEIGHT };
7511
- }
7512
- function renderLocation(result) {
7513
- const loc = result.locations?.[0]?.physicalLocation;
7514
- if (!loc) return void 0;
7515
- const uri = loc.artifactLocation?.uri;
7516
- const line = loc.region?.startLine;
7517
- if (!uri) return void 0;
7518
- return line != null ? `${uri}:${line}` : uri;
8987
+ const categoryScoreMap = {};
8988
+ for (const category of categoryOrder) {
8989
+ categoryScoreMap[category] = averageScore(
8990
+ criteria.filter((criterion) => criterion.category === category)
8991
+ );
8992
+ }
8993
+ const firstCategory = categoryOrder[0];
8994
+ const secondCategory = categoryOrder[1] ?? firstCategory;
8995
+ const codeTrustScore = categoryScoreMap.code_trust ?? (firstCategory ? categoryScoreMap[firstCategory] : void 0) ?? 0;
8996
+ const processTrustScore = categoryScoreMap.process_trust ?? (secondCategory ? categoryScoreMap[secondCategory] : void 0) ?? 0;
8997
+ const overallScore = roundScore(
8998
+ categoryOrder.reduce((sum, category) => sum + (categoryScoreMap[category] ?? 0), 0) / Math.max(categoryOrder.length, 1)
8999
+ );
9000
+ return WitanReportSchema.parse({
9001
+ productSlug: parsedInput.productSlug,
9002
+ productDisplayName: parsedInput.productDisplayName,
9003
+ repo: parsedInput.repo,
9004
+ generatedAt: parsedInput.generatedAt,
9005
+ rubricVersion: parsedInput.rubricVersion,
9006
+ codeTrustScore,
9007
+ processTrustScore,
9008
+ overallScore,
9009
+ criteria,
9010
+ ...consumedSignals.length > 0 ? { consumedSignals } : {},
9011
+ // Only surface the full per-category map for rubrics with more than two buckets —
9012
+ // the default two-category rubric is fully represented by codeTrustScore/processTrustScore.
9013
+ ...categoryOrder.length > 2 ? { categoryScores: categoryScoreMap } : {},
9014
+ // Pass through repo-archetype metadata verbatim — createWitanReport does not compute or
9015
+ // alter it (see classifyRepoArchetype in repo-signals.ts); per-criterion scoring above is
9016
+ // unaffected by archetype, only presentation layers (badge/terminal/verdict) key off it.
9017
+ ...parsedInput.archetype ? { archetype: parsedInput.archetype } : {},
9018
+ ...parsedInput.insufficientSourceReason ? { insufficientSourceReason: parsedInput.insufficientSourceReason } : {}
9019
+ });
9020
+ }
9021
+ function applySignalAdjustment(nativeScore, dimension, signals) {
9022
+ const summaries = [];
9023
+ let totalRawAdj = 0;
9024
+ for (const signal of signals) {
9025
+ const criticalCount = signal.findings.filter((f) => f.severity === "critical").length;
9026
+ const warningCount = signal.findings.filter((f) => f.severity === "warning").length;
9027
+ const infoCount = signal.findings.filter((f) => f.severity === "info").length;
9028
+ const severityScore = Math.min(
9029
+ criticalCount * 0.2 + warningCount * 0.1 + infoCount * 0.02,
9030
+ 1
9031
+ );
9032
+ const signalAdj = signal.weight * severityScore * MAX_SIGNAL_ADJUSTMENT;
9033
+ totalRawAdj += signalAdj;
9034
+ summaries.push({
9035
+ source: signal.source,
9036
+ dimension,
9037
+ findingCount: signal.findings.length,
9038
+ severityBreakdown: { critical: criticalCount, warning: warningCount, info: infoCount },
9039
+ nativeScore,
9040
+ scoreAdjustment: -roundScore(Math.min(signalAdj, MAX_SIGNAL_ADJUSTMENT)),
9041
+ adjustedScore: roundScore(
9042
+ Math.max(0, nativeScore - Math.min(signalAdj, MAX_SIGNAL_ADJUSTMENT))
9043
+ ),
9044
+ findings: [...signal.findings]
9045
+ });
9046
+ }
9047
+ const totalAdj = Math.min(totalRawAdj, MAX_SIGNAL_ADJUSTMENT);
9048
+ const adjustedScore = roundScore(Math.max(0, nativeScore - totalAdj));
9049
+ for (const summary of summaries) {
9050
+ summary.adjustedScore = adjustedScore;
9051
+ summary.scoreAdjustment = -roundScore(totalAdj);
9052
+ }
9053
+ return { adjustedScore, adjustment: roundScore(totalAdj), summaries };
7519
9054
  }
7520
- function dedupeFindings(findings) {
7521
- const seen = /* @__PURE__ */ new Set();
7522
- return findings.filter((f) => {
7523
- const key = `${f.ruleId}|${f.severity}|${f.location ?? ""}`;
7524
- if (seen.has(key)) return false;
7525
- seen.add(key);
7526
- return true;
7527
- });
9055
+ function statusForScore(score) {
9056
+ if (score >= 3.5) return "verified";
9057
+ if (score >= 2.5) return "info";
9058
+ if (score >= 1.5) return "warning";
9059
+ if (score > 0) return "critical";
9060
+ return "critical";
7528
9061
  }
7529
- function parseSarifJson(raw, extraDimensionRules) {
7530
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return [];
7531
- const dimensionRules = extraDimensionRules ? [...DIMENSION_RULES, ...extraDimensionRules] : DIMENSION_RULES;
7532
- const doc = raw;
7533
- if (!Array.isArray(doc.runs)) return [];
7534
- const buckets = /* @__PURE__ */ new Map();
7535
- for (const run of doc.runs) {
7536
- const driverName = run.tool?.driver?.name ?? "unknown";
7537
- const source = `sarif:${driverName}`;
7538
- const ruleDefaultLevels = /* @__PURE__ */ new Map();
7539
- for (const rule of run.tool?.driver?.rules ?? []) {
7540
- if (rule.id && rule.defaultConfiguration?.level) {
7541
- ruleDefaultLevels.set(rule.id, rule.defaultConfiguration.level);
7542
- }
7543
- }
7544
- for (const result of run.results ?? []) {
7545
- const ruleId = result.ruleId ?? "unknown";
7546
- const effectiveLevel = result.level ?? ruleDefaultLevels.get(ruleId) ?? "warning";
7547
- const severity = mapLevel(effectiveLevel);
7548
- if (!severity) continue;
7549
- const message = result.message?.text ?? ruleId;
7550
- const location = renderLocation(result);
7551
- const { dimension, weight } = mapRuleToDimension(ruleId, dimensionRules);
7552
- const bucketKey = `${source}|${dimension}`;
7553
- if (!buckets.has(bucketKey)) {
7554
- buckets.set(bucketKey, { source, dimension, weight, findings: [] });
7555
- }
7556
- buckets.get(bucketKey)?.findings.push({ ruleId, severity, message, ...location ? { location } : {} });
9062
+ function mergeSignalsByCriterion(signals) {
9063
+ const merged = /* @__PURE__ */ new Map();
9064
+ for (const signal of signals) {
9065
+ const existing = merged.get(signal.criterionId);
9066
+ if (!existing) {
9067
+ merged.set(signal.criterionId, signal);
9068
+ continue;
7557
9069
  }
9070
+ merged.set(signal.criterionId, {
9071
+ criterionId: signal.criterionId,
9072
+ positiveEvidence: [...existing.positiveEvidence, ...signal.positiveEvidence],
9073
+ findings: [...existing.findings, ...signal.findings],
9074
+ metrics: [...existing.metrics ?? [], ...signal.metrics ?? []],
9075
+ notes: signal.notes ?? existing.notes,
9076
+ // N/A wins on merge: if either signal is N/A, the criterion is N/A.
9077
+ ...existing.notApplicable === true || signal.notApplicable === true ? { notApplicable: true } : {}
9078
+ });
7558
9079
  }
7559
- return Array.from(buckets.values()).map(({ source, dimension, weight, findings }) => ({
7560
- source,
7561
- dimension,
7562
- weight,
7563
- findings: dedupeFindings(findings)
7564
- }));
9080
+ return merged;
7565
9081
  }
7566
-
7567
- // src/witan/scorecard-adapter.ts
7568
- import { readFileSync as readFileSync3 } from "fs";
7569
- var CHECK_RULES = {
7570
- // B2 — PR outcome traceability
7571
- "Branch-Protection": { dimension: "B2", weight: 0.8 },
7572
- "Code-Review": { dimension: "B2", weight: 0.7 },
7573
- // B3 — CI and QA discipline
7574
- "Dangerous-Workflow": { dimension: "B3", weight: 0.9 },
7575
- "Token-Permissions": { dimension: "B3", weight: 0.8 },
7576
- "CI-Tests": { dimension: "B3", weight: 0.7 },
7577
- Maintained: { dimension: "B3", weight: 0.3 },
7578
- // B4 — audit trail / release integrity
7579
- "Signed-Releases": { dimension: "B4", weight: 0.7 },
7580
- Packaging: { dimension: "B4", weight: 0.4 },
7581
- "Security-Policy": { dimension: "B4", weight: 0.4 },
7582
- // A4 — dependency hygiene
7583
- Vulnerabilities: { dimension: "A4", weight: 0.9 },
7584
- "Pinned-Dependencies": { dimension: "A4", weight: 0.8 },
7585
- "Dependency-Update-Tool": { dimension: "A4", weight: 0.6 },
7586
- // A2 — data-layer / static analysis
7587
- SAST: { dimension: "A2", weight: 0.7 },
7588
- "Binary-Artifacts": { dimension: "A2", weight: 0.6 },
7589
- // A1 — test integrity
7590
- Fuzzing: { dimension: "A1", weight: 0.3 }
7591
- };
7592
- function mapScoreToSeverity(score) {
7593
- if (score == null || Number.isNaN(score)) return null;
7594
- if (score < 0) return null;
7595
- if (score >= 10) return null;
7596
- if (score <= 3) return "critical";
7597
- if (score <= 6) return "warning";
7598
- return "info";
9082
+ function usesMetricScoring(rubricVersion) {
9083
+ return rubricVersion === WITAN_RUBRIC_VERSION_V1 || rubricVersion === WITAN_RUBRIC_VERSION_V2 || rubricVersion === WITAN_RUBRIC_VERSION_V3 || rubricVersion === WITAN_TRADING_RUBRIC_VERSION_V0;
7599
9084
  }
7600
- function dedupeFindings2(findings) {
7601
- const seen = /* @__PURE__ */ new Set();
7602
- return findings.filter((f) => {
7603
- const key = `${f.ruleId}|${f.severity}|${f.location ?? ""}`;
7604
- if (seen.has(key)) return false;
7605
- seen.add(key);
7606
- return true;
7607
- });
9085
+ function unmeasuredStatus(rubricVersion) {
9086
+ return rubricVersion === WITAN_TRADING_RUBRIC_VERSION_V0 ? "unverified" : "insufficient_data";
7608
9087
  }
7609
- function parseScorecardJson(raw) {
7610
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return [];
7611
- const doc = raw;
7612
- if (!Array.isArray(doc.checks)) return [];
7613
- const buckets = /* @__PURE__ */ new Map();
7614
- for (const check of doc.checks) {
7615
- const name = check.name;
7616
- if (!name) continue;
7617
- const rule = CHECK_RULES[name];
7618
- if (!rule) continue;
7619
- const severity = mapScoreToSeverity(check.score);
7620
- if (!severity) continue;
7621
- const scoreText = typeof check.score === "number" ? `${check.score}/10` : "n/a";
7622
- const message = `${name} (Scorecard ${scoreText}): ${check.reason ?? "weakness detected"}`.slice(0, 500);
7623
- const location = check.documentation?.url;
7624
- const bucket = buckets.get(rule.dimension) ?? { weight: 0, findings: [] };
7625
- bucket.weight = Math.max(bucket.weight, rule.weight);
7626
- bucket.findings.push({
7627
- ruleId: `scorecard:${name}`,
7628
- severity,
7629
- message,
7630
- ...location ? { location } : {}
7631
- });
7632
- buckets.set(rule.dimension, bucket);
9088
+ function scoreCriterion(signal, rubricVersion) {
9089
+ if (!signal) {
9090
+ return {
9091
+ score: 0,
9092
+ status: unmeasuredStatus(rubricVersion),
9093
+ evidence: [],
9094
+ findings: [],
9095
+ metrics: []
9096
+ };
7633
9097
  }
7634
- return Array.from(buckets.entries()).map(([dimension, { weight, findings }]) => ({
7635
- source: "scorecard",
7636
- dimension,
7637
- weight,
7638
- findings: dedupeFindings2(findings)
7639
- }));
7640
- }
7641
-
7642
- // src/witan/generic-adapter.ts
7643
- import { readFileSync as readFileSync4 } from "fs";
7644
- var VALID_DIMENSIONS = /* @__PURE__ */ new Set([
7645
- "A1",
7646
- "A2",
7647
- "A3",
7648
- "A4",
7649
- "A5",
7650
- "B1",
7651
- "B2",
7652
- "B3",
7653
- "B4",
7654
- "B5",
7655
- "B6"
7656
- ]);
7657
- var VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "warning", "info"]);
7658
- function isGenericSignalDocument(raw) {
7659
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
7660
- const doc = raw;
7661
- return typeof doc.tool === "string" && doc.tool.length > 0 && Array.isArray(doc.signals);
9098
+ if (signal.notApplicable === true) {
9099
+ return {
9100
+ score: 0,
9101
+ status: "not_applicable",
9102
+ evidence: [],
9103
+ findings: [],
9104
+ metrics: []
9105
+ };
9106
+ }
9107
+ const evidence = signal.positiveEvidence;
9108
+ const findings = signal.findings;
9109
+ const metrics = signal.metrics ?? [];
9110
+ const evidenceCount = evidence.length + findings.length;
9111
+ if (evidenceCount === 0) {
9112
+ if (!usesMetricScoring(rubricVersion) || metrics.length === 0) {
9113
+ return {
9114
+ score: 0,
9115
+ status: unmeasuredStatus(rubricVersion),
9116
+ evidence,
9117
+ findings,
9118
+ metrics
9119
+ };
9120
+ }
9121
+ }
9122
+ if (usesMetricScoring(rubricVersion)) {
9123
+ const measuredMetrics = metrics.length > 0 ? metrics : fallbackMetricsForEvidence(evidence, findings);
9124
+ const score = capScoreForFindings(scoreMetrics(measuredMetrics), findings);
9125
+ const status = statusForScoreAndFindings(score, findings);
9126
+ return {
9127
+ score,
9128
+ status,
9129
+ evidence,
9130
+ findings: ensureFindingsExplainStatus(signal.criterionId, score, status, findings, evidence),
9131
+ metrics: measuredMetrics
9132
+ };
9133
+ }
9134
+ if (findings.some((finding) => finding.severity === "critical")) {
9135
+ return {
9136
+ score: 1,
9137
+ status: "critical",
9138
+ evidence,
9139
+ findings,
9140
+ metrics
9141
+ };
9142
+ }
9143
+ if (findings.some((finding) => finding.severity === "warning")) {
9144
+ return {
9145
+ score: 2,
9146
+ status: "warning",
9147
+ evidence,
9148
+ findings,
9149
+ metrics
9150
+ };
9151
+ }
9152
+ if (findings.some((finding) => finding.severity === "info")) {
9153
+ return {
9154
+ score: 3,
9155
+ status: "info",
9156
+ evidence,
9157
+ findings,
9158
+ metrics
9159
+ };
9160
+ }
9161
+ return {
9162
+ score: 4,
9163
+ status: "verified",
9164
+ evidence,
9165
+ findings,
9166
+ metrics
9167
+ };
7662
9168
  }
7663
- function parseGenericJson(raw) {
7664
- if (!isGenericSignalDocument(raw)) return [];
7665
- const source = raw.tool ?? "unknown";
7666
- const signals = [];
7667
- for (const signal of raw.signals ?? []) {
7668
- const dimension = signal.dimension;
7669
- if (!dimension || !VALID_DIMENSIONS.has(dimension)) continue;
7670
- const weight = typeof signal.weight === "number" && Number.isFinite(signal.weight) ? Math.min(Math.max(signal.weight, 0), 1) : 0.5;
7671
- const findings = [];
7672
- for (const finding of signal.findings ?? []) {
7673
- const severity = finding.severity;
7674
- if (!severity || !VALID_SEVERITIES.has(severity)) continue;
7675
- const ruleId = finding.ruleId ?? "unknown";
7676
- findings.push({
7677
- ruleId,
7678
- severity,
7679
- message: finding.message ?? ruleId,
7680
- ...finding.location ? { location: finding.location } : {}
7681
- });
9169
+ function fallbackMetricsForEvidence(evidence, findings) {
9170
+ return [
9171
+ {
9172
+ name: "evidence_depth",
9173
+ label: "Evidence depth",
9174
+ value: Math.min(evidence.length + findings.length + 1, 4),
9175
+ max: 4,
9176
+ weight: 1,
9177
+ unit: "signals",
9178
+ description: "Conservative v1 fallback for externally supplied evidence that has not provided criterion-specific depth metrics."
7682
9179
  }
7683
- if (findings.length === 0) continue;
7684
- signals.push({ source, dimension, weight, findings });
7685
- }
7686
- return signals;
9180
+ ];
7687
9181
  }
7688
-
7689
- // src/witan/ingest.ts
7690
- import { readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
7691
- import { join as join2 } from "path";
7692
-
7693
- // src/witan/finding-limits.ts
7694
- var RULE_ID_MAX = 200;
7695
- var MESSAGE_MAX = 500;
7696
- var LOCATION_MAX = 700;
7697
- function clamp(value, max) {
7698
- if (value.length <= max) return value;
7699
- const ellipsis = "...";
7700
- return `${value.slice(0, Math.max(max - ellipsis.length, 0))}${ellipsis}`;
9182
+ function scoreMetrics(metrics) {
9183
+ let weightedTotal = 0;
9184
+ let totalWeight = 0;
9185
+ for (const metric2 of metrics) {
9186
+ const normalized = metric2.max ? Math.min(metric2.value / metric2.max, 1) : Math.min(metric2.value, 1);
9187
+ weightedTotal += normalized * metric2.weight;
9188
+ totalWeight += metric2.weight;
9189
+ }
9190
+ if (totalWeight === 0) return 0;
9191
+ return roundScore(weightedTotal / totalWeight * 4);
7701
9192
  }
7702
- function clampFinding(finding) {
7703
- return {
7704
- ...finding,
7705
- ruleId: clamp(finding.ruleId, RULE_ID_MAX),
7706
- message: clamp(finding.message, MESSAGE_MAX),
7707
- ...finding.location ? { location: clamp(finding.location, LOCATION_MAX) } : {}
7708
- };
9193
+ function capScoreForFindings(score, findings) {
9194
+ if (findings.some((finding) => finding.severity === "critical")) return 1.4;
9195
+ if (findings.some((finding) => finding.severity === "warning")) return Math.min(score, 2.4);
9196
+ if (findings.some((finding) => finding.severity === "info")) return Math.min(score, 3.4);
9197
+ return score;
7709
9198
  }
7710
-
7711
- // src/witan/ingest.ts
7712
- function looksLikeSarif(raw) {
7713
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
7714
- return Array.isArray(raw.runs);
9199
+ function statusForScoreAndFindings(score, findings) {
9200
+ if (findings.some((finding) => finding.severity === "critical")) return "critical";
9201
+ if (findings.some((finding) => finding.severity === "warning")) return "warning";
9202
+ if (score >= 3.5 && findings.length === 0) return "verified";
9203
+ if (score >= 2.5) return "info";
9204
+ if (score >= 1.5) return "warning";
9205
+ return "critical";
7715
9206
  }
7716
- function looksLikeScorecard(raw) {
7717
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
7718
- return Array.isArray(raw.checks);
9207
+ function ensureFindingsExplainStatus(criterionId, score, status, findings, evidence) {
9208
+ if (findings.length > 0) return [...findings];
9209
+ if (status !== "critical" && status !== "warning") return [...findings];
9210
+ const anchor = evidence[0] ?? {
9211
+ kind: "repository",
9212
+ label: `${criterionId} metric-derived score`,
9213
+ path: "."
9214
+ };
9215
+ return [
9216
+ {
9217
+ severity: status,
9218
+ summary: `${criterionId} metric-derived score is ${score.toFixed(1)}/4.0, in the ${status} band \u2014 no single finding drove this; it reflects the combined metric weighting below.`,
9219
+ evidence: anchor
9220
+ }
9221
+ ];
7719
9222
  }
7720
- function countRawEntries(raw) {
7721
- const obj = raw;
7722
- if (Array.isArray(obj.runs)) {
7723
- return obj.runs.reduce((sum, run) => {
7724
- const results = run.results;
7725
- return sum + (Array.isArray(results) ? results.length : 0);
7726
- }, 0);
7727
- }
7728
- if (Array.isArray(obj.checks)) return obj.checks.length;
7729
- if (Array.isArray(obj.signals)) {
7730
- return obj.signals.reduce((sum, signal) => {
7731
- const findings = signal.findings;
7732
- return sum + (Array.isArray(findings) ? findings.length : 0);
7733
- }, 0);
7734
- }
7735
- return 0;
9223
+ function averageScore(criteria) {
9224
+ const applicable = criteria.filter(
9225
+ (criterion) => criterion.status !== "not_applicable" && criterion.status !== "insufficient_data"
9226
+ );
9227
+ if (applicable.length === 0) return 0;
9228
+ const total = applicable.reduce((sum, criterion) => sum + criterion.score, 0);
9229
+ return roundScore(total / applicable.length);
7736
9230
  }
7737
- function rawToolName(raw) {
7738
- const obj = raw;
7739
- if (Array.isArray(obj.runs)) {
7740
- const firstRun = obj.runs[0];
7741
- return firstRun?.tool?.driver?.name ?? "unknown";
7742
- }
7743
- if (typeof obj.tool === "string" && obj.tool.length > 0) return obj.tool;
7744
- return "scorecard";
9231
+ function roundScore(score) {
9232
+ return Math.round(score * 10) / 10;
7745
9233
  }
7746
- function parseIngestFile(filePath) {
7747
- let raw;
7748
- try {
7749
- raw = JSON.parse(stripBom(readFileSync5(filePath, "utf8")));
7750
- } catch (error) {
7751
- const message = error instanceof Error ? error.message : String(error);
7752
- throw new Error(`Cejel: could not parse ingest file as JSON: ${filePath} (${message})`);
7753
- }
7754
- let signals;
7755
- if (looksLikeSarif(raw)) signals = parseSarifJson(raw);
7756
- else if (looksLikeScorecard(raw)) signals = parseScorecardJson(raw);
7757
- else if (isGenericSignalDocument(raw)) signals = parseGenericJson(raw);
7758
- if (!signals) {
7759
- throw new Error(
7760
- `Cejel: unrecognized ingest file format: ${filePath} \u2014 expected SARIF (a "runs" array), OpenSSF Scorecard JSON (a "checks" array), or the generic Cejel external-signal shape (a "tool" string + "signals" array). See the cejel README "Aggregate your scanners".`
7761
- );
7762
- }
7763
- const rawCount = countRawEntries(raw);
7764
- const mappedCount = signals.reduce((sum, signal) => sum + signal.findings.length, 0);
7765
- if (rawCount > 0 && mappedCount === 0) {
7766
- process.stderr.write(
7767
- `Cejel: ${rawCount} findings from ${rawToolName(raw)} (${filePath}), 0 mapped to trust criteria
7768
- `
7769
- );
7770
- }
7771
- return signals.map((signal) => ({
7772
- ...signal,
7773
- findings: signal.findings.map(clampFinding)
7774
- }));
9234
+
9235
+ // ../witan/src/public-scan.ts
9236
+ function scoreRepoWithPublicCejel(options) {
9237
+ const input = buildWitanInputFromRepo({
9238
+ productSlug: options.productSlug,
9239
+ productDisplayName: options.productDisplayName,
9240
+ repoPath: options.repoPath,
9241
+ ...options.generatedAt ? { generatedAt: options.generatedAt } : {}
9242
+ });
9243
+ const inputSignals = resolvePublicIngestSignals(options);
9244
+ return createWitanReport(input, inputSignals.length > 0 ? inputSignals : void 0);
7775
9245
  }
7776
- function expandIngestPattern(pattern) {
7777
- if (!pattern.includes("*")) return [pattern];
7778
- const lastSlash = pattern.lastIndexOf("/");
7779
- const dir = lastSlash === -1 ? "." : pattern.slice(0, lastSlash);
7780
- const filePattern = lastSlash === -1 ? pattern : pattern.slice(lastSlash + 1);
7781
- const regexSource = filePattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
7782
- const regex = new RegExp(`^${regexSource}$`);
7783
- let entries;
7784
- try {
7785
- entries = readdirSync2(dir);
7786
- } catch {
7787
- return [];
9246
+ function resolvePublicIngestSignals(options) {
9247
+ const seen = /* @__PURE__ */ new Set();
9248
+ const files = [];
9249
+ for (const pattern of options.ingestPatterns ?? []) {
9250
+ const matches = expandIngestPattern(pattern);
9251
+ if (matches.length === 0 && options.warnOnEmptyIngestMatch) {
9252
+ process.stderr.write(`Cejel: --ingest pattern matched no files: ${pattern}
9253
+ `);
9254
+ }
9255
+ for (const match of matches) {
9256
+ const resolved = resolve2(match);
9257
+ if (seen.has(resolved)) continue;
9258
+ seen.add(resolved);
9259
+ files.push(match);
9260
+ }
7788
9261
  }
7789
- return entries.filter((name) => regex.test(name)).sort().map((name) => join2(dir, name));
7790
- }
7791
- function discoverIngestInputs(repoPath) {
7792
- const inputsDir = join2(repoPath, ".cejel", "inputs");
7793
- let entries;
7794
- try {
7795
- entries = readdirSync2(inputsDir);
7796
- } catch {
7797
- return [];
9262
+ for (const discovered of discoverIngestInputs(options.repoPath)) {
9263
+ const resolved = resolve2(discovered);
9264
+ if (seen.has(resolved)) continue;
9265
+ seen.add(resolved);
9266
+ files.push(discovered);
7798
9267
  }
7799
- return entries.filter((name) => /\.(sarif|json)$/i.test(name)).sort().map((name) => join2(inputsDir, name));
9268
+ return files.flatMap((file) => parseIngestFile(file));
7800
9269
  }
7801
9270
 
7802
- // src/scan.ts
7803
- import { resolve as resolve2 } from "path";
9271
+ // ../witan/src/repo-clone.ts
9272
+ import { execFileSync as execFileSync2 } from "child_process";
9273
+ import { mkdirSync, rmSync } from "fs";
9274
+ import { tmpdir } from "os";
9275
+ import { join as join4 } from "path";
9276
+
9277
+ // ../witan/src/cejel-package-name.ts
9278
+ import { readFileSync as readFileSync7 } from "fs";
9279
+ import { fileURLToPath } from "url";
9280
+
9281
+ // ../witan/src/leaderboard.ts
9282
+ var RESERVED_CORPUS_SLUGS = ["biome", "tsconfig", "package"];
9283
+ var LeaderboardCorpusEntrySchema = external_exports.object({
9284
+ // Also the evidence-report slug (reports/<name>.{json,md,svg}) — min length 3
9285
+ // to satisfy the WitanReport productSlug pattern.
9286
+ name: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/).refine((value) => !RESERVED_CORPUS_SLUGS.includes(value), {
9287
+ message: "Corpus entry name collides with a tool config filename (reports/<name>.json would be discovered by that tool) \u2014 pick a different slug, e.g. biomejs."
9288
+ }),
9289
+ category: external_exports.string().min(1).max(80),
9290
+ expectedArchetype: external_exports.string().min(1).max(40),
9291
+ license: external_exports.string().min(1).max(80),
9292
+ /**
9293
+ * Repository visibility — the redaction policy's first-class input
9294
+ * (goal_cejel_redaction_policy_is_verifiability_2026-07-12): a path is
9295
+ * published exactly when the reader can check it (public), and withheld
9296
+ * entirely when they can't (private). FAIL CLOSED: absent or unrecognized
9297
+ * resolves to 'private' via `.catch` rather than throwing or defaulting
9298
+ * open, so a corpus entry can never accidentally publish paths by omission.
9299
+ */
9300
+ visibility: external_exports.enum(["public", "private"]).catch("private"),
9301
+ url: external_exports.string().url().optional(),
9302
+ /**
9303
+ * Immutable source snapshot for URL entries. A rubric rescore always uses
9304
+ * the same commits; changing one is a separate corpus update.
9305
+ */
9306
+ commit: external_exports.string().regex(/^[0-9a-f]{40}$/).optional(),
9307
+ self: external_exports.literal(true).optional(),
9308
+ /**
9309
+ * A directory inside THIS repository (relative to its root), for internal
9310
+ * entries that are a sub-package rather than the whole repo (e.g. the public
9311
+ * cejel CLI at packages/witan-cli). Scored locally like "self".
9312
+ */
9313
+ path: external_exports.string().min(1).max(200).regex(/^[A-Za-z0-9._/-]+$/).refine((value) => !value.startsWith("/") && !value.split("/").includes(".."), {
9314
+ message: 'Corpus entry "path" must be relative and must not traverse upward.'
9315
+ }).optional()
9316
+ }).strict().refine((entry) => (entry.url ? 1 : 0) + (entry.self ? 1 : 0) + (entry.path ? 1 : 0) === 1, {
9317
+ message: 'Corpus entry must have exactly one of "url", "self", or "path".'
9318
+ }).refine((entry) => entry.url ? entry.commit !== void 0 : entry.commit === void 0, {
9319
+ message: 'Every URL corpus entry must declare exactly one immutable 40-character "commit".'
9320
+ });
9321
+ var LeaderboardCorpusSchema = external_exports.object({
9322
+ comment: external_exports.string().min(1),
9323
+ entries: external_exports.array(LeaderboardCorpusEntrySchema).min(1)
9324
+ }).strict().refine(
9325
+ (corpus) => new Set(corpus.entries.map((entry) => entry.name)).size === corpus.entries.length,
9326
+ { message: "Corpus entry names must be unique." }
9327
+ );
9328
+
9329
+ // ../witan/src/quant-integrity.ts
9330
+ import { readFileSync as readFileSync10 } from "fs";
9331
+ import { join as join8 } from "path";
9332
+
9333
+ // ../witan/src/quant-metrics-grading.ts
9334
+ import { createHash as createHash2 } from "crypto";
9335
+ import { readFileSync as readFileSync9 } from "fs";
9336
+ import { join as join7 } from "path";
9337
+
9338
+ // ../witan/src/quant-run-tier-grading.ts
9339
+ import { createHash as createHash3 } from "crypto";
7804
9340
 
7805
9341
  // src/product-identity.ts
7806
- import { existsSync, readFileSync as readFileSync6 } from "fs";
7807
- import { basename as basename2, join as join3 } from "path";
9342
+ import { existsSync as existsSync3, readFileSync as readFileSync11 } from "fs";
9343
+ import { basename as basename3, join as join9 } from "path";
7808
9344
  var SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/;
7809
9345
  var FALLBACK_SLUG = "repo";
7810
9346
  var FALLBACK_DISPLAY_NAME = "Repository";
7811
9347
  function deriveProductIdentity(repoPath) {
7812
- const packageJsonPath = join3(repoPath, "package.json");
7813
- const raw = readPackageName(packageJsonPath) ?? basename2(repoPath);
9348
+ const packageJsonPath = join9(repoPath, "package.json");
9349
+ const raw = readPackageName(packageJsonPath) ?? basename3(repoPath);
7814
9350
  const displayName = raw.trim().length > 0 ? raw.trim() : FALLBACK_DISPLAY_NAME;
7815
9351
  return {
7816
9352
  productSlug: slugify(raw),
@@ -7818,9 +9354,9 @@ function deriveProductIdentity(repoPath) {
7818
9354
  };
7819
9355
  }
7820
9356
  function readPackageName(packageJsonPath) {
7821
- if (!existsSync(packageJsonPath)) return void 0;
9357
+ if (!existsSync3(packageJsonPath)) return void 0;
7822
9358
  try {
7823
- const parsed = JSON.parse(readFileSync6(packageJsonPath, "utf8"));
9359
+ const parsed = JSON.parse(readFileSync11(packageJsonPath, "utf8"));
7824
9360
  if (typeof parsed === "object" && parsed !== null && "name" in parsed) {
7825
9361
  const name = parsed.name;
7826
9362
  if (typeof name === "string" && name.trim().length > 0) return name;
@@ -7881,52 +9417,28 @@ function buildWitanCliSummary(report) {
7881
9417
  // src/scan.ts
7882
9418
  function runCejelScan(options) {
7883
9419
  const identity = deriveProductIdentity(options.repoPath);
7884
- const input = buildWitanInputFromRepo({
9420
+ const report = scoreRepoWithPublicCejel({
7885
9421
  productSlug: identity.productSlug,
7886
9422
  productDisplayName: identity.productDisplayName,
7887
- repoPath: options.repoPath
9423
+ repoPath: options.repoPath,
9424
+ ingestPatterns: options.ingestPatterns,
9425
+ warnOnEmptyIngestMatch: options.warnOnEmptyIngestMatch
7888
9426
  });
7889
- const inputSignals = resolveIngestSignals(options);
7890
- const report = createWitanReport(input, inputSignals.length > 0 ? inputSignals : void 0);
7891
9427
  const summary = buildWitanCliSummary(report);
7892
9428
  return { report, summary };
7893
9429
  }
7894
- function resolveIngestSignals(options) {
7895
- const seen = /* @__PURE__ */ new Set();
7896
- const files = [];
7897
- for (const pattern of options.ingestPatterns ?? []) {
7898
- const matches = expandIngestPattern(pattern);
7899
- if (matches.length === 0 && options.warnOnEmptyIngestMatch) {
7900
- process.stderr.write(`Cejel: --ingest pattern matched no files: ${pattern}
7901
- `);
7902
- }
7903
- for (const match of matches) {
7904
- const resolved = resolve2(match);
7905
- if (seen.has(resolved)) continue;
7906
- seen.add(resolved);
7907
- files.push(match);
7908
- }
7909
- }
7910
- for (const discovered of discoverIngestInputs(options.repoPath)) {
7911
- const resolved = resolve2(discovered);
7912
- if (seen.has(resolved)) continue;
7913
- seen.add(resolved);
7914
- files.push(discovered);
7915
- }
7916
- return files.flatMap((file) => parseIngestFile(file));
7917
- }
7918
9430
 
7919
9431
  export {
7920
9432
  __commonJS,
7921
9433
  __export,
7922
9434
  __toESM,
7923
- ZodOptional,
7924
- ZodFirstPartyTypeKind,
7925
- objectType,
7926
- external_exports,
7927
9435
  formatExternalSourceLine,
7928
9436
  renderWitanHtmlReport,
7929
9437
  renderWitanBadgeEndpoint,
7930
9438
  renderWitanBadgeSvg,
9439
+ ZodOptional,
9440
+ ZodFirstPartyTypeKind,
9441
+ objectType,
9442
+ external_exports,
7931
9443
  runCejelScan
7932
9444
  };