martin-loop 0.1.5 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CODE_OF_CONDUCT.md +32 -0
- package/LICENSE +21 -21
- package/README.md +307 -398
- package/demo/seeded-workspace/README.md +35 -35
- package/demo/seeded-workspace/TASKS.md +29 -29
- package/demo/seeded-workspace/martin.config.yaml +11 -11
- package/demo/seeded-workspace/package.json +8 -8
- package/demo/seeded-workspace/src/invoice-summary.js +11 -11
- package/demo/seeded-workspace/test/invoice-summary.test.js +20 -20
- package/dist/bin/martin-loop.js +0 -0
- package/dist/vendor/adapters/counter.d.ts +1 -0
- package/dist/vendor/adapters/counter.js +4 -0
- package/dist/vendor/adapters/git-baseline.d.ts +50 -0
- package/dist/vendor/adapters/git-baseline.js +233 -0
- package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
- package/dist/vendor/adapters/openrouter-adapter.js +302 -0
- package/dist/vendor/adapters/usage.d.ts +48 -0
- package/dist/vendor/adapters/usage.js +66 -0
- package/dist/vendor/cli/bin/exit.d.ts +12 -0
- package/dist/vendor/cli/bin/exit.js +28 -0
- package/dist/vendor/cli/commands/analyze.d.ts +5 -0
- package/dist/vendor/cli/commands/analyze.js +58 -0
- package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
- package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
- package/dist/vendor/cli/commands/audit.d.ts +8 -0
- package/dist/vendor/cli/commands/audit.js +199 -0
- package/dist/vendor/cli/commands/corpus.d.ts +5 -0
- package/dist/vendor/cli/commands/corpus.js +60 -0
- package/dist/vendor/cli/commands/doctor.d.ts +8 -0
- package/dist/vendor/cli/commands/doctor.js +219 -0
- package/dist/vendor/cli/commands/explain.d.ts +17 -0
- package/dist/vendor/cli/commands/explain.js +176 -0
- package/dist/vendor/cli/commands/export.d.ts +5 -0
- package/dist/vendor/cli/commands/export.js +60 -0
- package/dist/vendor/cli/commands/governance.d.ts +8 -0
- package/dist/vendor/cli/commands/governance.js +95 -0
- package/dist/vendor/cli/commands/improve.d.ts +18 -0
- package/dist/vendor/cli/commands/improve.js +396 -0
- package/dist/vendor/cli/commands/init.d.ts +8 -0
- package/dist/vendor/cli/commands/init.js +281 -0
- package/dist/vendor/cli/commands/migration.d.ts +8 -0
- package/dist/vendor/cli/commands/migration.js +67 -0
- package/dist/vendor/cli/commands/prior.d.ts +23 -0
- package/dist/vendor/cli/commands/prior.js +145 -0
- package/dist/vendor/cli/commands/resume.d.ts +21 -0
- package/dist/vendor/cli/commands/resume.js +73 -0
- package/dist/vendor/cli/commands/verify.d.ts +6 -0
- package/dist/vendor/cli/commands/verify.js +43 -0
- package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
- package/dist/vendor/cli/research/public-corpus.js +151 -0
- package/dist/vendor/cli/ui/error-card.d.ts +38 -0
- package/dist/vendor/cli/ui/error-card.js +103 -0
- package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
- package/dist/vendor/cli/ui/mission-brief.js +173 -0
- package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
- package/dist/vendor/cli/ui/summary-card.js +102 -0
- package/dist/vendor/contracts/audit.d.ts +46 -0
- package/dist/vendor/contracts/audit.js +360 -0
- package/dist/vendor/contracts/post-phase15.d.ts +240 -0
- package/dist/vendor/contracts/post-phase15.js +166 -0
- package/dist/vendor/core/agent/mandates.d.ts +46 -0
- package/dist/vendor/core/agent/mandates.js +178 -0
- package/dist/vendor/core/agent/receipts.d.ts +38 -0
- package/dist/vendor/core/agent/receipts.js +131 -0
- package/dist/vendor/core/agent/signing.d.ts +17 -0
- package/dist/vendor/core/agent/signing.js +91 -0
- package/dist/vendor/core/attestation/sign.d.ts +25 -0
- package/dist/vendor/core/attestation/sign.js +216 -0
- package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
- package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
- package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
- package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
- package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
- package/dist/vendor/core/autonomy/envelope.js +27 -0
- package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
- package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
- package/dist/vendor/core/autonomy/resume.d.ts +15 -0
- package/dist/vendor/core/autonomy/resume.js +23 -0
- package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
- package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
- package/dist/vendor/core/context-distillation.d.ts +3 -0
- package/dist/vendor/core/context-distillation.js +44 -0
- package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
- package/dist/vendor/core/context-flow/compile-context.js +111 -0
- package/dist/vendor/core/context-flow/entities.d.ts +2 -0
- package/dist/vendor/core/context-flow/entities.js +44 -0
- package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
- package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
- package/dist/vendor/core/context-flow/index.d.ts +11 -0
- package/dist/vendor/core/context-flow/index.js +24 -0
- package/dist/vendor/core/context-flow/labels.d.ts +3 -0
- package/dist/vendor/core/context-flow/labels.js +17 -0
- package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
- package/dist/vendor/core/context-flow/normalizer.js +69 -0
- package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
- package/dist/vendor/core/context-flow/profiles.js +36 -0
- package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
- package/dist/vendor/core/context-flow/redaction.js +6 -0
- package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
- package/dist/vendor/core/context-flow/sensitivity.js +27 -0
- package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
- package/dist/vendor/core/context-flow/sync-preview.js +22 -0
- package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
- package/dist/vendor/core/context-flow/token-estimator.js +13 -0
- package/dist/vendor/core/context-flow/types.d.ts +91 -0
- package/dist/vendor/core/context-flow/types.js +2 -0
- package/dist/vendor/core/context-utility.d.ts +47 -0
- package/dist/vendor/core/context-utility.js +405 -0
- package/dist/vendor/core/cost/pipeline.d.ts +92 -0
- package/dist/vendor/core/cost/pipeline.js +141 -0
- package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
- package/dist/vendor/core/cost/tagged-cost.js +55 -0
- package/dist/vendor/core/cost-governor.d.ts +2 -0
- package/dist/vendor/core/cost-governor.js +50 -0
- package/dist/vendor/core/cve/cve-check.d.ts +80 -0
- package/dist/vendor/core/cve/cve-check.js +172 -0
- package/dist/vendor/core/digital-twin/index.d.ts +27 -0
- package/dist/vendor/core/digital-twin/index.js +90 -0
- package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
- package/dist/vendor/core/drift/drift-graph.js +100 -0
- package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
- package/dist/vendor/core/drift/objective-lock.js +88 -0
- package/dist/vendor/core/drift/scope.d.ts +46 -0
- package/dist/vendor/core/drift/scope.js +102 -0
- package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
- package/dist/vendor/core/drift/signature-lock.js +202 -0
- package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
- package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
- package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
- package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
- package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
- package/dist/vendor/core/evidence/claim-audit.js +89 -0
- package/dist/vendor/core/exit-intelligence.d.ts +2 -0
- package/dist/vendor/core/exit-intelligence.js +58 -0
- package/dist/vendor/core/explain/formatter.d.ts +42 -0
- package/dist/vendor/core/explain/formatter.js +171 -0
- package/dist/vendor/core/explain/timeline.d.ts +29 -0
- package/dist/vendor/core/explain/timeline.js +213 -0
- package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
- package/dist/vendor/core/failure-taxonomy.js +76 -0
- package/dist/vendor/core/gateway/index.d.ts +10 -0
- package/dist/vendor/core/gateway/index.js +12 -0
- package/dist/vendor/core/gateway/registry.d.ts +40 -0
- package/dist/vendor/core/gateway/registry.js +97 -0
- package/dist/vendor/core/gateway/transport.d.ts +31 -0
- package/dist/vendor/core/gateway/transport.js +82 -0
- package/dist/vendor/core/gateway/vault.d.ts +19 -0
- package/dist/vendor/core/gateway/vault.js +29 -0
- package/dist/vendor/core/graph/adapters.d.ts +43 -0
- package/dist/vendor/core/graph/adapters.js +91 -0
- package/dist/vendor/core/graph/hotspots.d.ts +22 -0
- package/dist/vendor/core/graph/hotspots.js +30 -0
- package/dist/vendor/core/graph/index.d.ts +1 -0
- package/dist/vendor/core/graph/index.js +2 -0
- package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
- package/dist/vendor/core/honey/honey-tokens.js +44 -0
- package/dist/vendor/core/index.d.ts +2 -2
- package/dist/vendor/core/index.js +38 -12
- package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
- package/dist/vendor/core/learning/bayesian-update.js +60 -0
- package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
- package/dist/vendor/core/learning/prior-sets.js +111 -0
- package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
- package/dist/vendor/core/learning/promotion-gate.js +23 -0
- package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
- package/dist/vendor/core/leash/blast-radius.js +156 -0
- package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
- package/dist/vendor/core/leash/policy-leash.js +117 -0
- package/dist/vendor/core/memo/memo.d.ts +63 -0
- package/dist/vendor/core/memo/memo.js +97 -0
- package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
- package/dist/vendor/core/memory/learning-pipeline.js +391 -0
- package/dist/vendor/core/memory/palace.d.ts +84 -0
- package/dist/vendor/core/memory/palace.js +379 -0
- package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
- package/dist/vendor/core/merge/ast-merge.js +350 -0
- package/dist/vendor/core/merge/text-merge.d.ts +12 -0
- package/dist/vendor/core/merge/text-merge.js +182 -0
- package/dist/vendor/core/otel/tracer.d.ts +45 -0
- package/dist/vendor/core/otel/tracer.js +116 -0
- package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
- package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
- package/dist/vendor/core/parallel/scorer.d.ts +24 -0
- package/dist/vendor/core/parallel/scorer.js +65 -0
- package/dist/vendor/core/pattern-detection.d.ts +64 -0
- package/dist/vendor/core/pattern-detection.js +108 -0
- package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
- package/dist/vendor/core/persistence/checkpoint.js +156 -0
- package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
- package/dist/vendor/core/persistence/cleanup.js +131 -0
- package/dist/vendor/core/persistence/index.d.ts +2 -0
- package/dist/vendor/core/persistence/index.js +1 -0
- package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
- package/dist/vendor/core/persistence/runs-reader.js +84 -0
- package/dist/vendor/core/persistence/store.d.ts +6 -1
- package/dist/vendor/core/persistence/store.js +5 -0
- package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
- package/dist/vendor/core/policy/file-touch-quota.js +105 -0
- package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
- package/dist/vendor/core/policy/policy-loader.js +170 -0
- package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
- package/dist/vendor/core/policy/policy-schema.js +78 -0
- package/dist/vendor/core/probe/probe.d.ts +49 -0
- package/dist/vendor/core/probe/probe.js +115 -0
- package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
- package/dist/vendor/core/proof/patch-proof.js +84 -0
- package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
- package/dist/vendor/core/proof/semantic-probe.js +82 -0
- package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
- package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
- package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
- package/dist/vendor/core/red-blue/red-phase.js +141 -0
- package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
- package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
- package/dist/vendor/core/replay/replay.d.ts +85 -0
- package/dist/vendor/core/replay/replay.js +109 -0
- package/dist/vendor/core/router/engine.d.ts +54 -0
- package/dist/vendor/core/router/engine.js +131 -0
- package/dist/vendor/core/router/index.d.ts +1 -0
- package/dist/vendor/core/router/index.js +2 -0
- package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
- package/dist/vendor/core/router/trust-calibration.js +127 -0
- package/dist/vendor/core/run-martin.d.ts +2 -0
- package/dist/vendor/core/run-martin.js +287 -0
- package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
- package/dist/vendor/core/security/cve-scanner.js +178 -0
- package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
- package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
- package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
- package/dist/vendor/core/sentinel/progress-guard.js +46 -0
- package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
- package/dist/vendor/core/siem/siem-emitter.js +157 -0
- package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
- package/dist/vendor/core/strategy/attempt-brief.js +89 -0
- package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
- package/dist/vendor/core/summarize/diff-summary.js +204 -0
- package/dist/vendor/core/surface-signals.d.ts +21 -0
- package/dist/vendor/core/surface-signals.js +139 -0
- package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
- package/dist/vendor/core/truth/truth-wall.js +69 -0
- package/dist/vendor/core/truth-spine.d.ts +26 -0
- package/dist/vendor/core/truth-spine.js +62 -0
- package/dist/vendor/core/types.d.ts +115 -0
- package/dist/vendor/core/types.js +2 -0
- package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
- package/dist/vendor/core/verification/tiered-verify.js +29 -0
- package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
- package/dist/vendor/core/verifier-pyramid.js +111 -0
- package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
- package/dist/vendor/core/workflow-artifacts.js +668 -0
- package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
- package/dist/vendor/core/wrap/supervised-run.js +178 -0
- package/docs/assets/cli-animated.svg +139 -0
- package/docs/assets/cli-static.svg +34 -0
- package/docs/assets/github-hero-v2.svg +23 -0
- package/docs/assets/martin-raplph.png.jpg +0 -0
- package/docs/assets/martinloop-logo.png +0 -0
- package/docs/assets/nvidia-inception-program-light.png +0 -0
- package/docs/assets/nvidia-inception-program.png +0 -0
- package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
- package/docs/assets/side-by-side.svg +134 -0
- package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -142
- package/docs/oss/EXAMPLES.md +134 -134
- package/docs/oss/OSS-BOUNDARY-REPORT.json +1 -1
- package/docs/oss/OSS-BOUNDARY-REPORT.md +1 -1
- package/docs/oss/QUICKSTART.md +170 -165
- package/docs/oss/RALPH-LOOP-SAFETY.md +113 -113
- package/docs/oss/README.md +96 -96
- package/docs/oss/RELEASE-SURFACE-REPORT.json +2 -1
- package/docs/oss/RELEASE-SURFACE-REPORT.md +2 -1
- package/package.json +130 -58
- package/docs/distribution/DIRECTORY-SUBMISSIONS.md +0 -89
- package/docs/distribution/INTEGRATION-OUTREACH.md +0 -61
- package/docs/distribution/UNDER-3-CHALLENGE.md +0 -65
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight adjacency-list graph used by scope pruning (SLICE-02).
|
|
3
|
+
* Nodes are file paths (or symbol names). Edges represent import/reference
|
|
4
|
+
* relationships: for each node, the array contains its direct dependents.
|
|
5
|
+
*/
|
|
6
|
+
export interface DriftGraph {
|
|
7
|
+
/** Complete set of known node identifiers (file paths or symbol names). */
|
|
8
|
+
nodes: Set<string>;
|
|
9
|
+
/** Adjacency list: node → list of directly connected nodes (1 hop). */
|
|
10
|
+
edges: Record<string, string[]>;
|
|
11
|
+
}
|
|
12
|
+
export interface DriftReport {
|
|
13
|
+
changedExports: string[];
|
|
14
|
+
affectedImporters: Array<{
|
|
15
|
+
file: string;
|
|
16
|
+
importedSymbol: string;
|
|
17
|
+
}>;
|
|
18
|
+
/** Importers with no test coverage for the changed symbol */
|
|
19
|
+
unvalidatedDownstream: string[];
|
|
20
|
+
/** true only if unvalidatedDownstream is empty */
|
|
21
|
+
safe: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface DriftInput {
|
|
24
|
+
/** Workspace root — ts-morph will glob all .ts files under it */
|
|
25
|
+
rootDir: string;
|
|
26
|
+
/** Path of the changed file, relative to rootDir (forward slashes) */
|
|
27
|
+
changedFile: string;
|
|
28
|
+
/** List of exported symbol names that changed */
|
|
29
|
+
changedExports: string[];
|
|
30
|
+
/** List of test file paths (absolute or relative to rootDir) that cover the changed symbols */
|
|
31
|
+
testFiles: string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Builds a DriftReport by walking the workspace with ts-morph.
|
|
35
|
+
*
|
|
36
|
+
* Algorithm:
|
|
37
|
+
* 1. If changedExports is empty → safe: true immediately (no public API touched)
|
|
38
|
+
* 2. Walk all .ts files (excluding node_modules) looking for imports of any changed symbol
|
|
39
|
+
* 3. For each importer, check if a test file also imports the same changed symbol
|
|
40
|
+
* 4. Importers without test coverage → unvalidatedDownstream → safe: false
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildDriftReport(input: DriftInput): Promise<DriftReport>;
|
|
43
|
+
/**
|
|
44
|
+
* Builds and writes drift-report.json to the run output directory.
|
|
45
|
+
* Only writes if changedExports is non-empty (no drift = no report needed).
|
|
46
|
+
*/
|
|
47
|
+
export declare function emitDriftReport(input: DriftInput, runDir: string): Promise<DriftReport>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { Project } from "ts-morph";
|
|
4
|
+
// ─── Core builder ─────────────────────────────────────────────────────────────
|
|
5
|
+
/**
|
|
6
|
+
* Builds a DriftReport by walking the workspace with ts-morph.
|
|
7
|
+
*
|
|
8
|
+
* Algorithm:
|
|
9
|
+
* 1. If changedExports is empty → safe: true immediately (no public API touched)
|
|
10
|
+
* 2. Walk all .ts files (excluding node_modules) looking for imports of any changed symbol
|
|
11
|
+
* 3. For each importer, check if a test file also imports the same changed symbol
|
|
12
|
+
* 4. Importers without test coverage → unvalidatedDownstream → safe: false
|
|
13
|
+
*/
|
|
14
|
+
export async function buildDriftReport(input) {
|
|
15
|
+
if (input.changedExports.length === 0) {
|
|
16
|
+
return {
|
|
17
|
+
changedExports: [],
|
|
18
|
+
affectedImporters: [],
|
|
19
|
+
unvalidatedDownstream: [],
|
|
20
|
+
safe: true
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const project = new Project({
|
|
24
|
+
skipAddingFilesFromTsConfig: true
|
|
25
|
+
});
|
|
26
|
+
// Add all .ts files from rootDir (not node_modules, not .d.ts)
|
|
27
|
+
project.addSourceFilesAtPaths([
|
|
28
|
+
normalizePath(join(input.rootDir, "**/*.ts")),
|
|
29
|
+
`!${normalizePath(join(input.rootDir, "**/node_modules/**"))}`,
|
|
30
|
+
`!${normalizePath(join(input.rootDir, "**/*.d.ts"))}`
|
|
31
|
+
]);
|
|
32
|
+
const changedFilePath = normalizePath(join(input.rootDir, input.changedFile));
|
|
33
|
+
const affectedImporters = [];
|
|
34
|
+
for (const sourceFile of project.getSourceFiles()) {
|
|
35
|
+
const filePath = normalizePath(sourceFile.getFilePath());
|
|
36
|
+
// Skip the changed file itself
|
|
37
|
+
if (filePath === changedFilePath)
|
|
38
|
+
continue;
|
|
39
|
+
// Check import declarations for any of the changed symbols
|
|
40
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
41
|
+
const namedImports = importDecl.getNamedImports();
|
|
42
|
+
for (const namedImport of namedImports) {
|
|
43
|
+
const symbolName = namedImport.getName();
|
|
44
|
+
if (input.changedExports.includes(symbolName)) {
|
|
45
|
+
affectedImporters.push({
|
|
46
|
+
file: filePath,
|
|
47
|
+
importedSymbol: symbolName
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Determine which importers are test files (by path convention or explicit list)
|
|
54
|
+
const testFilePaths = new Set(input.testFiles.map(f => normalizePath(f.startsWith("/") || /^[A-Za-z]:/.test(f)
|
|
55
|
+
? f
|
|
56
|
+
: join(input.rootDir, f))));
|
|
57
|
+
// Also treat files matching *.test.ts or *.spec.ts as test files
|
|
58
|
+
const testSymbolsCovered = new Set();
|
|
59
|
+
for (const importer of affectedImporters) {
|
|
60
|
+
const isTestFile = testFilePaths.has(importer.file) ||
|
|
61
|
+
/\.(test|spec)\.[tj]s$/.test(importer.file);
|
|
62
|
+
if (isTestFile) {
|
|
63
|
+
testSymbolsCovered.add(importer.importedSymbol);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// unvalidatedDownstream = non-test importers whose symbol has no test coverage
|
|
67
|
+
const unvalidatedDownstream = [];
|
|
68
|
+
for (const importer of affectedImporters) {
|
|
69
|
+
const isTestFile = testFilePaths.has(importer.file) ||
|
|
70
|
+
/\.(test|spec)\.[tj]s$/.test(importer.file);
|
|
71
|
+
if (!isTestFile && !testSymbolsCovered.has(importer.importedSymbol)) {
|
|
72
|
+
if (!unvalidatedDownstream.includes(importer.file)) {
|
|
73
|
+
unvalidatedDownstream.push(importer.file);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
changedExports: input.changedExports,
|
|
79
|
+
affectedImporters,
|
|
80
|
+
unvalidatedDownstream,
|
|
81
|
+
safe: unvalidatedDownstream.length === 0
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// ─── Emitter ──────────────────────────────────────────────────────────────────
|
|
85
|
+
/**
|
|
86
|
+
* Builds and writes drift-report.json to the run output directory.
|
|
87
|
+
* Only writes if changedExports is non-empty (no drift = no report needed).
|
|
88
|
+
*/
|
|
89
|
+
export async function emitDriftReport(input, runDir) {
|
|
90
|
+
const report = await buildDriftReport(input);
|
|
91
|
+
if (input.changedExports.length > 0) {
|
|
92
|
+
await writeFile(join(runDir, "drift-report.json"), JSON.stringify(report, null, 2), "utf8");
|
|
93
|
+
}
|
|
94
|
+
return report;
|
|
95
|
+
}
|
|
96
|
+
// ─── Internal helpers ─────────────────────────────────────────────────────────
|
|
97
|
+
function normalizePath(p) {
|
|
98
|
+
return p.replace(/\\/g, "/");
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=drift-graph.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* objective-lock.ts — SLICE-05
|
|
3
|
+
*
|
|
4
|
+
* Cryptographic objective invariant lock.
|
|
5
|
+
*
|
|
6
|
+
* At attempt 1, the loop objective, verificationPlan, and acceptanceCriteria
|
|
7
|
+
* are hashed together into a SHA-256 lock. Every subsequent attempt re-hashes
|
|
8
|
+
* the live inputs and hard-fails with `objective_drift` if the hash differs.
|
|
9
|
+
*
|
|
10
|
+
* Legitimate scope changes require an explicit `widenObjectiveLock()` call,
|
|
11
|
+
* which produces an audit entry proving who authorized the change and why.
|
|
12
|
+
*/
|
|
13
|
+
export interface ObjectiveLockInput {
|
|
14
|
+
objective: string;
|
|
15
|
+
verificationPlan: string[];
|
|
16
|
+
acceptanceCriteria?: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface ObjectiveLock {
|
|
19
|
+
/** SHA-256 hex digest of the canonical lock payload. */
|
|
20
|
+
sha256: string;
|
|
21
|
+
/** ISO timestamp when the lock was created. */
|
|
22
|
+
lockedAt: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ObjectiveLockCheckResult {
|
|
25
|
+
drifted: boolean;
|
|
26
|
+
/** Present only when drifted:true. */
|
|
27
|
+
reason?: string;
|
|
28
|
+
expectedHash?: string;
|
|
29
|
+
actualHash?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ObjectiveWidenAuditEntry {
|
|
32
|
+
previousHash: string;
|
|
33
|
+
newHash: string;
|
|
34
|
+
authorizedBy: string;
|
|
35
|
+
reason: string;
|
|
36
|
+
widenedAt: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ObjectiveWidenResult {
|
|
39
|
+
newLock: ObjectiveLock;
|
|
40
|
+
auditEntry: ObjectiveWidenAuditEntry;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates a new ObjectiveLock for the given inputs.
|
|
44
|
+
* Call once at loop start (attempt 1). Store in the loop record.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createObjectiveLock(input: ObjectiveLockInput, now?: string): ObjectiveLock;
|
|
47
|
+
/**
|
|
48
|
+
* Checks whether the live inputs still match the lock created at attempt 1.
|
|
49
|
+
*
|
|
50
|
+
* Returns `{ drifted: false }` if everything is consistent.
|
|
51
|
+
* Returns `{ drifted: true, reason, expectedHash, actualHash }` if the lock
|
|
52
|
+
* has been violated — the caller MUST exit the loop with lifecycleState
|
|
53
|
+
* `objective_drift`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function checkObjectiveLock(lock: ObjectiveLock, currentInput: ObjectiveLockInput): ObjectiveLockCheckResult;
|
|
56
|
+
/**
|
|
57
|
+
* Authorizes a legitimate scope change. Returns a new lock covering the
|
|
58
|
+
* new inputs and an immutable audit entry recording who widened it and why.
|
|
59
|
+
*
|
|
60
|
+
* The audit entry MUST be appended to the loop's ledger.jsonl before the
|
|
61
|
+
* run continues with the new lock.
|
|
62
|
+
*/
|
|
63
|
+
export declare function widenObjectiveLock(options: {
|
|
64
|
+
currentLock: ObjectiveLock;
|
|
65
|
+
newInput: ObjectiveLockInput;
|
|
66
|
+
authorizedBy: string;
|
|
67
|
+
reason: string;
|
|
68
|
+
now?: string;
|
|
69
|
+
}): ObjectiveWidenResult;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* objective-lock.ts — SLICE-05
|
|
3
|
+
*
|
|
4
|
+
* Cryptographic objective invariant lock.
|
|
5
|
+
*
|
|
6
|
+
* At attempt 1, the loop objective, verificationPlan, and acceptanceCriteria
|
|
7
|
+
* are hashed together into a SHA-256 lock. Every subsequent attempt re-hashes
|
|
8
|
+
* the live inputs and hard-fails with `objective_drift` if the hash differs.
|
|
9
|
+
*
|
|
10
|
+
* Legitimate scope changes require an explicit `widenObjectiveLock()` call,
|
|
11
|
+
* which produces an audit entry proving who authorized the change and why.
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Public API
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new ObjectiveLock for the given inputs.
|
|
19
|
+
* Call once at loop start (attempt 1). Store in the loop record.
|
|
20
|
+
*/
|
|
21
|
+
export function createObjectiveLock(input, now = new Date().toISOString()) {
|
|
22
|
+
return {
|
|
23
|
+
sha256: computeHash(input),
|
|
24
|
+
lockedAt: now
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Checks whether the live inputs still match the lock created at attempt 1.
|
|
29
|
+
*
|
|
30
|
+
* Returns `{ drifted: false }` if everything is consistent.
|
|
31
|
+
* Returns `{ drifted: true, reason, expectedHash, actualHash }` if the lock
|
|
32
|
+
* has been violated — the caller MUST exit the loop with lifecycleState
|
|
33
|
+
* `objective_drift`.
|
|
34
|
+
*/
|
|
35
|
+
export function checkObjectiveLock(lock, currentInput) {
|
|
36
|
+
const currentHash = computeHash(currentInput);
|
|
37
|
+
if (currentHash === lock.sha256) {
|
|
38
|
+
return { drifted: false };
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
drifted: true,
|
|
42
|
+
reason: `Objective lock violated: expected hash ${lock.sha256.slice(0, 12)}… ` +
|
|
43
|
+
`but got ${currentHash.slice(0, 12)}… — run aborted with lifecycle state objective_drift`,
|
|
44
|
+
expectedHash: lock.sha256,
|
|
45
|
+
actualHash: currentHash
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Authorizes a legitimate scope change. Returns a new lock covering the
|
|
50
|
+
* new inputs and an immutable audit entry recording who widened it and why.
|
|
51
|
+
*
|
|
52
|
+
* The audit entry MUST be appended to the loop's ledger.jsonl before the
|
|
53
|
+
* run continues with the new lock.
|
|
54
|
+
*/
|
|
55
|
+
export function widenObjectiveLock(options) {
|
|
56
|
+
const widenedAt = options.now ?? new Date().toISOString();
|
|
57
|
+
const newLock = createObjectiveLock(options.newInput, widenedAt);
|
|
58
|
+
const auditEntry = {
|
|
59
|
+
previousHash: options.currentLock.sha256,
|
|
60
|
+
newHash: newLock.sha256,
|
|
61
|
+
authorizedBy: options.authorizedBy,
|
|
62
|
+
reason: options.reason,
|
|
63
|
+
widenedAt
|
|
64
|
+
};
|
|
65
|
+
return { newLock, auditEntry };
|
|
66
|
+
}
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Internal — canonical hash computation
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
/**
|
|
71
|
+
* Produces a deterministic SHA-256 hex digest over:
|
|
72
|
+
* - objective (exact text, case-sensitive)
|
|
73
|
+
* - verificationPlan (ordered — step order matters)
|
|
74
|
+
* - acceptanceCriteria (sorted — order-independent)
|
|
75
|
+
*
|
|
76
|
+
* The canonical payload is a stable JSON string to ensure cross-platform
|
|
77
|
+
* determinism regardless of property insertion order.
|
|
78
|
+
*/
|
|
79
|
+
function computeHash(input) {
|
|
80
|
+
const sortedCriteria = [...(input.acceptanceCriteria ?? [])].sort();
|
|
81
|
+
const canonical = JSON.stringify({
|
|
82
|
+
objective: input.objective,
|
|
83
|
+
verificationPlan: input.verificationPlan,
|
|
84
|
+
acceptanceCriteria: sortedCriteria
|
|
85
|
+
});
|
|
86
|
+
return createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=objective-lock.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scope.ts — SLICE-02
|
|
3
|
+
*
|
|
4
|
+
* Drift-graph–scoped context pruning.
|
|
5
|
+
*
|
|
6
|
+
* Uses the existing DriftGraph to prune model input context to only the files
|
|
7
|
+
* within N hops of the objective's named symbols/paths. Prevents the model
|
|
8
|
+
* from seeing irrelevant files and keeps token budgets tight.
|
|
9
|
+
*/
|
|
10
|
+
import type { DriftGraph } from "./drift-graph.js";
|
|
11
|
+
export interface ScopePruningResult {
|
|
12
|
+
/** The pruned (or fallback) file list to pass to the model. */
|
|
13
|
+
files: string[];
|
|
14
|
+
/** Number of files in the original compiledFiles list. */
|
|
15
|
+
filesIn: number;
|
|
16
|
+
/** Number of files returned after pruning. */
|
|
17
|
+
filesOut: number;
|
|
18
|
+
/** filesIn - filesOut */
|
|
19
|
+
prunedCount: number;
|
|
20
|
+
/** true when the BFS intersection was empty and we fell back to the full set. */
|
|
21
|
+
fallback: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Extracts file-like identifiers from an objective string.
|
|
25
|
+
*
|
|
26
|
+
* Matches (in priority order):
|
|
27
|
+
* 1. Quoted paths — "src/parser.ts"
|
|
28
|
+
* 2. Bare paths ending in .ts / .js / .tsx / .jsx
|
|
29
|
+
* 3. camelCase / PascalCase tokens that look like module/symbol names
|
|
30
|
+
*
|
|
31
|
+
* Note: intentionally uses regex, not AST (that's SLICE-06).
|
|
32
|
+
*/
|
|
33
|
+
export declare function extractFilesFromObjective(objective: string): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Prunes `compiledFiles` to only those reachable from the objective's seed
|
|
36
|
+
* nodes within `maxHops` traversal steps in `graph`.
|
|
37
|
+
*
|
|
38
|
+
* Falls back to the full compiledFiles set when the BFS result has no
|
|
39
|
+
* intersection with compiledFiles (e.g. graph uses different path conventions).
|
|
40
|
+
*/
|
|
41
|
+
export declare function scopeContextByDriftGraph(options: {
|
|
42
|
+
objective: string;
|
|
43
|
+
graph: DriftGraph;
|
|
44
|
+
compiledFiles: string[];
|
|
45
|
+
maxHops?: number;
|
|
46
|
+
}): ScopePruningResult;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scope.ts — SLICE-02
|
|
3
|
+
*
|
|
4
|
+
* Drift-graph–scoped context pruning.
|
|
5
|
+
*
|
|
6
|
+
* Uses the existing DriftGraph to prune model input context to only the files
|
|
7
|
+
* within N hops of the objective's named symbols/paths. Prevents the model
|
|
8
|
+
* from seeing irrelevant files and keeps token budgets tight.
|
|
9
|
+
*/
|
|
10
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Extracts file-like identifiers from an objective string.
|
|
13
|
+
*
|
|
14
|
+
* Matches (in priority order):
|
|
15
|
+
* 1. Quoted paths — "src/parser.ts"
|
|
16
|
+
* 2. Bare paths ending in .ts / .js / .tsx / .jsx
|
|
17
|
+
* 3. camelCase / PascalCase tokens that look like module/symbol names
|
|
18
|
+
*
|
|
19
|
+
* Note: intentionally uses regex, not AST (that's SLICE-06).
|
|
20
|
+
*/
|
|
21
|
+
export function extractFilesFromObjective(objective) {
|
|
22
|
+
const found = new Set();
|
|
23
|
+
// 1. Quoted file paths — single or double quotes
|
|
24
|
+
const quotedRe = /["']([^"'\n]+(?:\.(?:ts|tsx|js|jsx)|[A-Za-z0-9_/.-]+))["']/g;
|
|
25
|
+
for (const match of objective.matchAll(quotedRe)) {
|
|
26
|
+
if (match[1])
|
|
27
|
+
found.add(match[1]);
|
|
28
|
+
}
|
|
29
|
+
// 2. Bare paths ending in a recognised extension (not already captured in quotes)
|
|
30
|
+
const barePathRe = /(?<![/"'])([^\s"']+\.(?:ts|tsx|js|jsx))(?![/"'])/g;
|
|
31
|
+
for (const match of objective.matchAll(barePathRe)) {
|
|
32
|
+
if (match[1])
|
|
33
|
+
found.add(match[1]);
|
|
34
|
+
}
|
|
35
|
+
// 3. camelCase / PascalCase identifiers (potential module / symbol names)
|
|
36
|
+
// Must start with an uppercase letter or have an internal uppercase letter,
|
|
37
|
+
// and be at least 4 chars so we avoid common short words.
|
|
38
|
+
const camelCaseRe = /\b(?:[A-Z][a-zA-Z0-9]{3,}|[a-z][a-z0-9]*[A-Z][a-zA-Z0-9]{2,})\b/g;
|
|
39
|
+
for (const match of objective.matchAll(camelCaseRe)) {
|
|
40
|
+
found.add(match[0]);
|
|
41
|
+
}
|
|
42
|
+
return [...found];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Prunes `compiledFiles` to only those reachable from the objective's seed
|
|
46
|
+
* nodes within `maxHops` traversal steps in `graph`.
|
|
47
|
+
*
|
|
48
|
+
* Falls back to the full compiledFiles set when the BFS result has no
|
|
49
|
+
* intersection with compiledFiles (e.g. graph uses different path conventions).
|
|
50
|
+
*/
|
|
51
|
+
export function scopeContextByDriftGraph(options) {
|
|
52
|
+
const { objective, graph, compiledFiles, maxHops = 2 } = options;
|
|
53
|
+
const filesIn = compiledFiles.length;
|
|
54
|
+
// Extract seed identifiers from objective text
|
|
55
|
+
const seeds = extractFilesFromObjective(objective);
|
|
56
|
+
// BFS from all seed nodes that exist in the graph
|
|
57
|
+
const reachable = new Set();
|
|
58
|
+
const queue = [];
|
|
59
|
+
for (const seed of seeds) {
|
|
60
|
+
if (graph.nodes.has(seed)) {
|
|
61
|
+
queue.push({ node: seed, depth: 0 });
|
|
62
|
+
reachable.add(seed);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Process BFS queue
|
|
66
|
+
let head = 0;
|
|
67
|
+
while (head < queue.length) {
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
69
|
+
const item = queue[head++];
|
|
70
|
+
if (item.depth >= maxHops)
|
|
71
|
+
continue;
|
|
72
|
+
const neighbors = graph.edges[item.node] ?? [];
|
|
73
|
+
for (const neighbor of neighbors) {
|
|
74
|
+
if (!reachable.has(neighbor)) {
|
|
75
|
+
reachable.add(neighbor);
|
|
76
|
+
queue.push({ node: neighbor, depth: item.depth + 1 });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Intersect BFS result with compiledFiles (preserve original ordering)
|
|
81
|
+
const compiledSet = new Set(compiledFiles);
|
|
82
|
+
const pruned = compiledFiles.filter(f => reachable.has(f));
|
|
83
|
+
// Fallback: if intersection is empty return the full original set
|
|
84
|
+
if (pruned.length === 0) {
|
|
85
|
+
return {
|
|
86
|
+
files: [...compiledFiles],
|
|
87
|
+
filesIn,
|
|
88
|
+
filesOut: filesIn,
|
|
89
|
+
prunedCount: 0,
|
|
90
|
+
fallback: true
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const filesOut = pruned.length;
|
|
94
|
+
return {
|
|
95
|
+
files: pruned,
|
|
96
|
+
filesIn,
|
|
97
|
+
filesOut,
|
|
98
|
+
prunedCount: filesIn - filesOut,
|
|
99
|
+
fallback: false
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* signature-lock.ts — SLICE-06
|
|
3
|
+
*
|
|
4
|
+
* AST-level signature lock: extracts named symbols from an objective,
|
|
5
|
+
* snapshots their TypeScript signatures before the first attempt, then
|
|
6
|
+
* rejects any patch that silently changes a signature without authorization.
|
|
7
|
+
*
|
|
8
|
+
* Authorization phrases in the objective (e.g. "add parameter", "rename")
|
|
9
|
+
* allow signature changes for the named symbol.
|
|
10
|
+
*/
|
|
11
|
+
export interface SignatureSnapshot {
|
|
12
|
+
symbolName: string;
|
|
13
|
+
filePath: string;
|
|
14
|
+
params: string[];
|
|
15
|
+
returnType: string;
|
|
16
|
+
modifiers: string[];
|
|
17
|
+
kind: "function" | "method" | "class" | "unknown";
|
|
18
|
+
}
|
|
19
|
+
export interface SignatureLockResult {
|
|
20
|
+
blocked: boolean;
|
|
21
|
+
reasonCode?: "silent_signature_drift";
|
|
22
|
+
reason?: string;
|
|
23
|
+
driftedSymbols: Array<{
|
|
24
|
+
symbolName: string;
|
|
25
|
+
before: SignatureSnapshot;
|
|
26
|
+
after: SignatureSnapshot;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
export declare function isSignatureChangeAuthorized(objective: string, symbolName: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Extracts likely symbol names from the objective string.
|
|
32
|
+
* Looks for: camelCase, PascalCase, snake_case, and `backtick` quoted names.
|
|
33
|
+
*/
|
|
34
|
+
export declare function extractSymbolsFromObjective(objective: string): string[];
|
|
35
|
+
export declare function snapshotsEqual(a: SignatureSnapshot, b: SignatureSnapshot): boolean;
|
|
36
|
+
export declare class SignatureLockStore {
|
|
37
|
+
private snapshots;
|
|
38
|
+
record(snapshot: SignatureSnapshot): void;
|
|
39
|
+
get(symbolName: string): SignatureSnapshot | undefined;
|
|
40
|
+
has(symbolName: string): boolean;
|
|
41
|
+
clear(): void;
|
|
42
|
+
}
|
|
43
|
+
export declare function checkSignatureDrift(objective: string, before: SignatureSnapshot[], after: SignatureSnapshot[]): SignatureLockResult;
|
|
44
|
+
/**
|
|
45
|
+
* Extracts SignatureSnapshots for named symbols from TypeScript source code.
|
|
46
|
+
* Uses ts-morph if available; falls back to a regex-based approximation.
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractSignaturesFromSource(sourceCode: string, filePath: string, targetSymbols: string[]): Promise<SignatureSnapshot[]>;
|