@mrciphersmith/keryx 0.2.32 → 0.2.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +170 -2
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -35991,6 +35991,165 @@ async function resolvePolicySelectionSafely(workspaceRoot, canonicalFallback) {
35991
35991
  return canonicalFallback;
35992
35992
  }
35993
35993
  }
35994
+ async function diagnosePolicyReadiness(workspaceRoot) {
35995
+ const steps = [];
35996
+ const msg = (error2) => error2 instanceof Error ? error2.message : String(error2);
35997
+ const pass = (step, detail) => {
35998
+ steps.push(detail === undefined ? { step, status: "pass" } : { step, status: "pass", detail });
35999
+ };
36000
+ const fail = (step, detail) => {
36001
+ steps.push({ step, status: "fail", detail });
36002
+ };
36003
+ const finalize = (configPresent, enabled2, killSwitch2) => {
36004
+ const integrityReady = steps.every((entry) => entry.step === "activation-flags" || entry.status === "pass");
36005
+ return Object.freeze({ configPresent, enabled: enabled2, killSwitch: killSwitch2, integrityReady, candidateWouldActivate: integrityReady && enabled2 && !killSwitch2, steps: Object.freeze([...steps]) });
36006
+ };
36007
+ const configPath3 = path118.join(workspaceRoot, ...policyExperimentConfigPath);
36008
+ let configJson;
36009
+ try {
36010
+ const raw = await readFile64(configPath3, "utf8").catch((error2) => {
36011
+ if (isMissing(error2))
36012
+ return;
36013
+ throw error2;
36014
+ });
36015
+ if (raw === undefined) {
36016
+ fail("config", "no policy-experiment config at .metaproject/context-operations/policy-experiment/config.json");
36017
+ return finalize(false, false, true);
36018
+ }
36019
+ const parsed = JSON.parse(raw);
36020
+ if (!isRecord3(parsed) || !isConfigRecord(parsed)) {
36021
+ fail("config", "config is not a valid policy-experiment record (needs boolean enabled + killSwitch)");
36022
+ return finalize(true, false, true);
36023
+ }
36024
+ configJson = parsed;
36025
+ } catch (error2) {
36026
+ fail("config", `config could not be read or parsed: ${msg(error2)}`);
36027
+ return finalize(true, false, true);
36028
+ }
36029
+ pass("config");
36030
+ const enabled = configJson.enabled === true;
36031
+ const killSwitch = configJson.killSwitch !== false;
36032
+ if (enabled && !killSwitch)
36033
+ pass("activation-flags", "enabled and kill-switch released");
36034
+ else
36035
+ fail("activation-flags", `candidate stays off by config: enabled=${String(configJson.enabled)}, killSwitch=${String(configJson.killSwitch)}`);
36036
+ const requiredPins = ["candidateArtifactRef", "candidateArtifactDigest", "candidateVersion", "baselineArtifactRef", "baselineArtifactDigest", "baselineVersion", "corpusRef", "corpusDigest", "corpusVersion", "evaluationReportRef", "evaluationDigest"];
36037
+ const missing = requiredPins.filter((name) => typeof configJson[name] !== "string");
36038
+ if (missing.length > 0) {
36039
+ fail("config-pins", `missing or non-string pins: ${missing.join(", ")}`);
36040
+ return finalize(true, enabled, killSwitch);
36041
+ }
36042
+ const candidateRef = configJson.candidateArtifactRef;
36043
+ const candidateDigest = configJson.candidateArtifactDigest;
36044
+ const baselineRef = configJson.baselineArtifactRef;
36045
+ const baselineDigest = configJson.baselineArtifactDigest;
36046
+ const corpusRef = configJson.corpusRef;
36047
+ const corpusDigest = configJson.corpusDigest;
36048
+ const evaluationRef = configJson.evaluationReportRef;
36049
+ const evaluationDigest = configJson.evaluationDigest;
36050
+ if (!asString4(candidateRef) || !asString4(candidateDigest) || !asString4(baselineRef) || !asString4(baselineDigest) || !asString4(corpusRef) || !asString4(corpusDigest) || !asString4(evaluationRef) || !asString4(evaluationDigest)) {
36051
+ fail("config-pins", "pins are present but not non-empty strings");
36052
+ return finalize(true, enabled, killSwitch);
36053
+ }
36054
+ pass("config-pins");
36055
+ const refsOk = workspacePathPattern3.test(candidateRef) && workspacePathPattern3.test(baselineRef) && workspacePathPattern3.test(corpusRef) && workspacePathPattern3.test(evaluationRef);
36056
+ if (refsOk)
36057
+ pass("reference-paths");
36058
+ else
36059
+ fail("reference-paths", "one or more artifact refs are not contained workspace-relative './\u2026' paths");
36060
+ const hashesOk = validHash(candidateDigest) && validHash(baselineDigest) && validHash(corpusDigest) && validHash(evaluationDigest);
36061
+ if (hashesOk)
36062
+ pass("digest-format");
36063
+ else
36064
+ fail("digest-format", "one or more pinned digests are not sha256 hex");
36065
+ const versionsOk = isImmutableVersion2(configJson.candidateVersion) && isImmutableVersion2(configJson.baselineVersion) && isImmutableVersion2(configJson.corpusVersion);
36066
+ if (versionsOk)
36067
+ pass("immutable-versions");
36068
+ else
36069
+ fail("immutable-versions", "candidate/baseline/corpus versions must be immutable (no latest/main/head/\u2026)");
36070
+ const policyRefsOk = policyRefPattern.test(candidateRef) && policyRefPattern.test(baselineRef);
36071
+ if (policyRefsOk)
36072
+ pass("policy-refs");
36073
+ else
36074
+ fail("policy-refs", "candidate/baseline refs must be './\u2026' policy references");
36075
+ if (!(refsOk && hashesOk && versionsOk && policyRefsOk))
36076
+ return finalize(true, enabled, killSwitch);
36077
+ let baselineArtifact;
36078
+ let candidateArtifact;
36079
+ let corpusPayload;
36080
+ let evaluationPayload;
36081
+ try {
36082
+ baselineArtifact = await readPinnedJson({ workspaceRoot, uri: baselineRef, digest: baselineDigest });
36083
+ if (isBaselineArtifact(baselineArtifact.artifact))
36084
+ pass("baseline-artifact");
36085
+ else
36086
+ fail("baseline-artifact", "not a valid deterministic-baseline artifact");
36087
+ } catch (error2) {
36088
+ fail("baseline-artifact", `unreadable or digest mismatch: ${msg(error2)}`);
36089
+ }
36090
+ try {
36091
+ candidateArtifact = await readPinnedJson({ workspaceRoot, uri: candidateRef, digest: candidateDigest });
36092
+ if (isCandidateArtifact(candidateArtifact.artifact))
36093
+ pass("candidate-artifact");
36094
+ else
36095
+ fail("candidate-artifact", "not a valid offline-selection-advisor artifact");
36096
+ } catch (error2) {
36097
+ fail("candidate-artifact", `unreadable or digest mismatch: ${msg(error2)}`);
36098
+ }
36099
+ try {
36100
+ corpusPayload = await readWorkspaceJson({ workspaceRoot, uri: corpusRef });
36101
+ if (!isCompleteCorpus(corpusPayload.artifact))
36102
+ fail("corpus", "corpus failed structural / manifest verification");
36103
+ else if (corpusPayload.artifact.manifest.corpusVersion !== configJson.corpusVersion || corpusPayload.artifact.manifest.corpusDigest !== corpusDigest)
36104
+ fail("corpus", "corpus manifest version/digest does not match pins");
36105
+ else
36106
+ pass("corpus");
36107
+ } catch (error2) {
36108
+ fail("corpus", `unreadable: ${msg(error2)}`);
36109
+ }
36110
+ try {
36111
+ evaluationPayload = await readWorkspaceJson({ workspaceRoot, uri: evaluationRef });
36112
+ if (!isEvaluationReport(evaluationPayload.artifact) || !hasReportIntegrity(evaluationPayload.artifact))
36113
+ fail("evaluation-report", "report is malformed or its recomputed digest does not match");
36114
+ else if (evaluationPayload.artifact.reportDigest !== evaluationDigest)
36115
+ fail("evaluation-report", "report digest does not match pin");
36116
+ else
36117
+ pass("evaluation-report");
36118
+ } catch (error2) {
36119
+ fail("evaluation-report", `unreadable: ${msg(error2)}`);
36120
+ }
36121
+ if (baselineArtifact && candidateArtifact && corpusPayload && evaluationPayload && isBaselineArtifact(baselineArtifact.artifact) && isCandidateArtifact(candidateArtifact.artifact) && isCompleteCorpus(corpusPayload.artifact) && isEvaluationReport(evaluationPayload.artifact) && hasReportIntegrity(evaluationPayload.artifact) && corpusPayload.artifact.manifest.corpusVersion === configJson.corpusVersion && corpusPayload.artifact.manifest.corpusDigest === corpusDigest && evaluationPayload.artifact.reportDigest === evaluationDigest) {
36122
+ try {
36123
+ const config = {
36124
+ ...configJson,
36125
+ enabled: true,
36126
+ killSwitch: false,
36127
+ candidateArtifactDigest: candidateArtifact.digest,
36128
+ candidateDigest: candidateArtifact.digest,
36129
+ baselineArtifactDigest: baselineArtifact.digest,
36130
+ baselineDigest: baselineArtifact.digest,
36131
+ corpusDigest,
36132
+ evaluationDigest,
36133
+ candidateVersion: configJson.candidateVersion,
36134
+ baselineVersion: configJson.baselineVersion,
36135
+ corpusVersion: configJson.corpusVersion,
36136
+ rollbackBaselineVersion: configJson.rollbackBaselineVersion ?? configJson.baselineVersion
36137
+ };
36138
+ const baseline = { selectedIds: evaluationPayload.artifact.candidateSelectedIds, source: "deterministic-baseline", version: baselineArtifact.artifact.version, artifactDigest: baselineArtifact.digest };
36139
+ const candidate = { version: candidateArtifact.artifact.version, artifactDigest: candidateArtifact.digest };
36140
+ const outcome = resolvePolicyExperiment({ config, evaluation: evaluationPayload.artifact, candidate, corpus: corpusPayload.artifact, baseline });
36141
+ if (outcome.source === "candidate")
36142
+ pass("activation-gate", "evidence gates pass: security non-regression, holdout, adversarial, pins and candidate subset");
36143
+ else
36144
+ fail("activation-gate", `evidence gates reject candidate; report reasons: ${evaluationPayload.artifact.reasons.join(", ") || "none"}`);
36145
+ } catch (error2) {
36146
+ fail("activation-gate", `activation check errored: ${msg(error2)}`);
36147
+ }
36148
+ } else {
36149
+ fail("activation-gate", "skipped: one or more prerequisite artifact checks did not pass");
36150
+ }
36151
+ return finalize(true, enabled, killSwitch);
36152
+ }
35994
36153
  function isCompleteCorpus(value) {
35995
36154
  return isRecord3(value) && isRecord3(value.manifest) && Array.isArray(value.rows) && Array.isArray(value.quarantine) && verifyPolicyCorpus(value);
35996
36155
  }
@@ -48466,7 +48625,7 @@ init_shell_config();
48466
48625
  // package.json
48467
48626
  var package_default = {
48468
48627
  name: "@mrciphersmith/keryx",
48469
- version: "0.2.32",
48628
+ version: "0.2.33",
48470
48629
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
48471
48630
  private: false,
48472
48631
  publishConfig: {
@@ -54294,6 +54453,14 @@ async function workspaceCommand(args2) {
54294
54453
  console.log(JSON.stringify(await createLocalCollaborationService(process.cwd()).overview({ workspaceId, request: undefined, requestCorrelationId: randomUUID20() }), null, 2));
54295
54454
  return;
54296
54455
  }
54456
+ if (subcommand === "policy-readiness") {
54457
+ rejectUnknownOptions(args2.slice(1), new Set);
54458
+ const report = await diagnosePolicyReadiness(process.cwd());
54459
+ console.log(JSON.stringify(report, null, 2));
54460
+ if (!report.integrityReady)
54461
+ process.exitCode = 1;
54462
+ return;
54463
+ }
54297
54464
  throw new Error(`Unknown workspace command: ${subcommand}`);
54298
54465
  } catch (error2) {
54299
54466
  console.error(error2 instanceof Error ? error2.message : String(error2));
@@ -54318,7 +54485,8 @@ keryx workspace overview <workspace-id> [--max-items N] [--max-tokens N]
54318
54485
  keryx workspace read <workspace-id> <item-id> [--max-items N] [--max-tokens N]
54319
54486
  keryx workspace propose <workspace-id> --kind <kind> --summary <explicit-summary> --evidence <workspace-relative-ref> [--revision <revision>]
54320
54487
  keryx workspace review <workspace-id> <proposal-id> --decision <accepted|rejected|dismissed> [--reason <reason>] [--idempotency-key <key>]
54321
- keryx workspace collaboration <workspace-id>`);
54488
+ keryx workspace collaboration <workspace-id>
54489
+ keryx workspace policy-readiness`);
54322
54490
  }
54323
54491
 
54324
54492
  // src/cli.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.2.32",
3
+ "version": "0.2.33",
4
4
  "description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
5
5
  "private": false,
6
6
  "publishConfig": {