@clear-capabilities/agentic-security-scanner 0.162.0 → 0.163.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/CHANGELOG.md +26 -0
- package/bin/agentic-security.js +87 -0
- package/dist/1340.index.js +1871 -0
- package/dist/3477.index.js +727 -0
- package/dist/3499.index.js +23 -3
- package/dist/4113.index.js +1 -1
- package/dist/5014.index.js +833 -0
- package/dist/5756.index.js +2 -2
- package/dist/6257.index.js +1 -1
- package/dist/6907.index.js +248 -0
- package/dist/6994.index.js +1 -1
- package/dist/7033.index.js +33 -5
- package/dist/7439.index.js +1822 -0
- package/dist/7838.index.js +1 -1
- package/dist/9560.index.js +2318 -23
- package/dist/957.index.js +1 -1
- package/dist/9999.index.js +43 -7
- package/dist/agentic-security.mjs +12 -12
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +33 -6
- package/src/capabilities/CLAUDE.md +114 -0
- package/src/capabilities/address.js +73 -0
- package/src/capabilities/attack-coverage.js +93 -0
- package/src/capabilities/decide.js +256 -0
- package/src/capabilities/delegate.js +69 -0
- package/src/capabilities/egress.js +20 -0
- package/src/capabilities/hook-advice.js +50 -0
- package/src/capabilities/index.js +32 -0
- package/src/capabilities/manifest.js +344 -0
- package/src/capabilities/outbound.js +189 -0
- package/src/capabilities/paths.js +57 -0
- package/src/capabilities/probes.js +211 -0
- package/src/capabilities/proxy.js +182 -0
- package/src/capabilities/reasons.js +67 -0
- package/src/capabilities/receipts.js +356 -0
- package/src/capabilities/records.js +58 -0
- package/src/capabilities/recovery.js +367 -0
- package/src/capabilities/report.js +81 -0
- package/src/capabilities/runner.js +236 -0
- package/src/capabilities/secrets.js +26 -0
- package/src/capabilities/tool-gate.js +119 -0
- package/src/capabilities/tool-registry.js +60 -0
- package/src/dataflow/CLAUDE.md +29 -0
- package/src/dataflow/catalog.js +21 -0
- package/src/dataflow/engine.js +20 -0
- package/src/dataflow/guard-dominance.js +352 -0
- package/src/discovery/CLAUDE.md +9 -0
- package/src/discovery/index.js +15 -1
- package/src/discovery/lenses.js +18 -0
- package/src/engine.js +80 -15
- package/src/integrations/index.js +5 -2
- package/src/ir/CLAUDE.md +14 -0
- package/src/ir/implicit-handler-params.js +74 -0
- package/src/ir/parser-cs.js +20 -0
- package/src/ir/parser-go.js +30 -13
- package/src/ir/parser-java.js +11 -0
- package/src/language/nix-eval-isolation.js +33 -5
- package/src/lineage/CLAUDE.md +31 -0
- package/src/lineage/deployment/adapter-cloud.js +202 -0
- package/src/lineage/deployment/adapter-compose.js +90 -0
- package/src/lineage/deployment/adapter-kit.js +142 -0
- package/src/lineage/deployment/adapter-kubernetes.js +247 -0
- package/src/lineage/deployment/attack-paths.js +443 -0
- package/src/lineage/deployment/boundaries-run.js +223 -0
- package/src/lineage/deployment/boundary-graph.js +567 -0
- package/src/lineage/deployment/cross-repo.js +253 -0
- package/src/lineage/deployment/drift.js +383 -0
- package/src/lineage/deployment/ingest.js +221 -0
- package/src/lineage/deployment/projection.js +244 -0
- package/src/lineage/deployment/trace-correlation.js +541 -0
- package/src/lineage/source-registry.js +19 -0
- package/src/mcp/CLAUDE.md +5 -2
- package/src/mcp/invariant-tools.js +64 -0
- package/src/mcp/portfolio-tools.js +79 -0
- package/src/mcp/redact.js +15 -0
- package/src/mcp/server.js +24 -3
- package/src/mcp/tools.js +13 -1
- package/src/posture/CLAUDE.md +332 -1
- package/src/posture/accuracy-scorecard.js +95 -4
- package/src/posture/assurance/rollback.js +214 -0
- package/src/posture/assurance/rollout.js +200 -0
- package/src/posture/business-logic.js +26 -4
- package/src/posture/evaluation/bakeoff.js +148 -0
- package/src/posture/evaluation/custody.js +200 -0
- package/src/posture/evaluation/deployment-ablation.js +416 -0
- package/src/posture/evaluation/dev-cases.js +65 -0
- package/src/posture/evaluation/economics.js +85 -0
- package/src/posture/evaluation/gates.js +161 -0
- package/src/posture/evaluation/grouping.js +101 -0
- package/src/posture/evaluation/interval.js +132 -0
- package/src/posture/evaluation/invariant-ablation.js +325 -0
- package/src/posture/evaluation/labels.js +200 -0
- package/src/posture/evaluation/protocol.js +308 -0
- package/src/posture/evaluation/release-gate.js +209 -0
- package/src/posture/evaluation/report.js +158 -0
- package/src/posture/evaluation/runner.js +273 -0
- package/src/posture/evaluation/scan-child.js +80 -0
- package/src/posture/evaluation/score.js +174 -0
- package/src/posture/evaluation/synthetic.js +90 -0
- package/src/posture/evaluation/why-missed.js +219 -0
- package/src/posture/fleet.js +11 -2
- package/src/posture/invariants/cli.js +95 -0
- package/src/posture/invariants/coverage.js +164 -0
- package/src/posture/invariants/export.js +178 -0
- package/src/posture/invariants/expressions.js +46 -0
- package/src/posture/invariants/lifecycle.js +289 -0
- package/src/posture/invariants/project-input.js +55 -0
- package/src/posture/invariants/repair.js +340 -0
- package/src/posture/invariants/run.js +45 -0
- package/src/posture/invariants/scenarios.js +311 -0
- package/src/posture/invariants/schema.js +167 -0
- package/src/posture/invariants/shrink.js +407 -0
- package/src/posture/invariants/state-assertions.js +165 -0
- package/src/posture/logic-claims.js +12 -0
- package/src/posture/model-routing.js +40 -0
- package/src/posture/oracles/adapters.js +172 -1
- package/src/posture/oracles/conformance.js +3 -2
- package/src/posture/oracles/oracle.js +23 -3
- package/src/posture/oracles/registry.js +1 -1
- package/src/posture/portfolio/backend.js +279 -0
- package/src/posture/portfolio/bundle.js +314 -0
- package/src/posture/portfolio/cli.js +142 -0
- package/src/posture/portfolio/manifest.js +263 -0
- package/src/posture/portfolio/progress.js +267 -0
- package/src/posture/portfolio/resume.js +130 -0
- package/src/posture/portfolio/retention.js +207 -0
- package/src/posture/portfolio/scheduler.js +488 -0
- package/src/posture/portfolio/signing.js +155 -0
- package/src/posture/portfolio/wording.js +59 -0
- package/src/posture/portfolio/work-units.js +405 -0
- package/src/posture/replay/replay.js +1 -0
- package/src/posture/routing/adapter.js +140 -0
- package/src/posture/routing/calibration.js +278 -0
- package/src/posture/routing/canary.js +141 -0
- package/src/posture/routing/control.js +35 -0
- package/src/posture/routing/decide.js +211 -0
- package/src/posture/routing/drift.js +137 -0
- package/src/posture/routing/economics.js +185 -0
- package/src/posture/routing/feedback.js +207 -0
- package/src/posture/routing/index.js +31 -0
- package/src/posture/routing/outcomes.js +281 -0
- package/src/posture/routing/promotion.js +291 -0
- package/src/posture/routing/receipts.js +59 -0
- package/src/posture/routing/report.js +142 -0
- package/src/posture/routing/shadow.js +53 -0
- package/src/posture/specification-mining.js +26 -0
- package/src/posture/verification/patch-negative.js +16 -4
- package/src/posture/verification/patch-promotion.js +10 -2
- package/src/report/index.js +51 -1
- package/src/sandbox/CLAUDE.md +10 -0
- package/src/sandbox/backend-userspace.js +91 -13
- package/dist/5613.index.js +0 -500
- package/dist/6239.index.js +0 -469
package/src/posture/CLAUDE.md
CHANGED
|
@@ -141,7 +141,7 @@ one was caught by the module's own test, not in review.
|
|
|
141
141
|
- **No throwing.** Every annotation in `engine.js` is wrapped `try { … } catch (_) {}`. Your annotator must degrade gracefully — set `null` on the field and continue.
|
|
142
142
|
- **Dead-module test.** `npm run test:lifecycle` fails the build if you export a public symbol from a posture module that no other source file imports. Wire it in `engine.js` (or allowlist it with a written reason in `test/no-dead-modules.test.js`).
|
|
143
143
|
|
|
144
|
-
## Assurance contracts, baseline and rollout config — `assurance/` (
|
|
144
|
+
## Assurance contracts, baseline and rollout config — `assurance/` (11 modules)
|
|
145
145
|
|
|
146
146
|
Foundation for the differentiation work (CORE-001, CORE-002, CORE-004). All of it is pure or read-only, and **nothing in it is wired into `engine.js` or the scan/report path**: a default scan is byte-for-byte unaffected, which `test/posture/assurance-config.test.js` pins. Consumers (X-201 and later) import it directly.
|
|
147
147
|
|
|
@@ -154,6 +154,19 @@ Foundation for the differentiation work (CORE-001, CORE-002, CORE-004). All of i
|
|
|
154
154
|
- **`bounded-io.js`**: `guardedModelCall` is the only route for new model/network calls. It owns no HTTP client (the transport is injected): feature gate, then `evaluateEgress` BEFORE the payload exists, then `redactPayload`, then the request-size limit, then deadline plus bounded retries, then output cap, with every decision appended to the existing egress audit chain. `readFileBounded`, `capOutput`, `withDeadline`, `retryBounded` enforce the limits `config.js` claims; `maxMemoryMiB` is carried for a runner and disclosed as not enforced here.
|
|
155
155
|
- **`baseline.js`, `baseline-inventory.js`**: baseline capture (`scripts/baseline-capture.mjs`, `npm run baseline:capture`). Read-only; records HEAD, dirty paths, exact source and bundle digests, tool versions, entry points, the seven mappings and an 18-entry capability inventory (implemented / partial / unsupported / unmeasured, every path and exported symbol verified at capture time, a missing one is a reported problem). Unknown facts are `{status:'unknown', reason}` with no value. `evaluateBaseline` invalidates exactly the capabilities whose cited files changed and lists unrelated user changes as retained/introduced. The manifest is machine-specific, so it is generated on demand and not committed. When you change a capability's source or tests, update its inventory entry in the same change.
|
|
156
156
|
|
|
157
|
+
- **`rollout.js`, `rollback.js` (REL-002).** Staged rollout and rollback. `rollout.js`: the three stages (`offline-fixtures`, `shadow-canary`,
|
|
158
|
+
`opt-in-supported`), the documented gates per feature and stage (`gatesFor`, `describeRollout`), an append-only ledger (`newLedger`), `promote`
|
|
159
|
+
(one stage at a time, every gate needs evidence that names its own suite and carries a sha256; the kill switch and an unsupported platform
|
|
160
|
+
block it; policy versions are never reused) and `effectiveMode` (an enabled feature below opt-in runs as `fixtures` or `shadow` and can never
|
|
161
|
+
change an outcome). `rollback.js`: `READER_POLICY` (per record kind, whether a previous reader is retained; four kinds honestly say no),
|
|
162
|
+
`planRollback`/`applyRollback` (restore the last known-good stage, append a `rollback` event, FLAG evidence and claims `stale` or
|
|
163
|
+
`incompatible`, never delete or rewrite), `EvidenceStore`/`guardedFeatureRun` (append-only, a thrown feature records a failure and costs no
|
|
164
|
+
evidence), and the known break: `verifyWithPreCore003Reader` is a faithful copy of the verifier before the `issuance` block existed, which
|
|
165
|
+
rejects every newly signed bundle; `exportLegacyBundle` is the migration path (verifies the original first, re-signs without issuance,
|
|
166
|
+
returns a sidecar carrying the dropped trust basis). Pure, consumed by tests and `docs/guides/assurance-rollout-and-rollback.md`; nothing in
|
|
167
|
+
the scan path imports them. Suite: `test/release-closure/` (`npm run test:release-closure`). The release closure itself (the plan, the
|
|
168
|
+
commit-bound record, the judge) is `scripts/release-closure.mjs`, not a posture module.
|
|
169
|
+
|
|
157
170
|
Tests: `test/posture/assurance-{baseline,contracts,config}.test.js`, all in `test:posture`.
|
|
158
171
|
|
|
159
172
|
## One verification record, trusted oracles and replay (X-201, X-202, X-203)
|
|
@@ -313,6 +326,324 @@ receipts, three tamper attempts, an unavailable prerequisite, cancellation, repl
|
|
|
313
326
|
(`verification-conformance-static`). Where the boundary cannot run, execution is reported `not-run`, never passed; `--require-execution`
|
|
314
327
|
makes that a failure. A new adapter must be pinned deliberately (`--update-pins`).
|
|
315
328
|
|
|
329
|
+
## Frozen evaluation protocol, label custody and layer ablations: `evaluation/` (QA-001, QA-002, QA-003)
|
|
330
|
+
|
|
331
|
+
Offline measurement tooling, consumed by `scripts/evaluation.mjs` (`npm run evaluation -- <cmd>`, `npm run evaluation:synthetic`) and
|
|
332
|
+
`test:evaluation`. Nothing here is wired into `engine.js` or the scan path, and a test pins that no module outside `evaluation/`
|
|
333
|
+
imports it. **It builds machinery, not evidence.** No real adjudicated population, no real sealed set and no independent human
|
|
334
|
+
review exist in this repository, so every real-code gate reads `insufficient-population` or `unmeasured`; the exercised suite
|
|
335
|
+
(`synthetic.js`, fixtures in `test/fixtures/evaluation-synthetic/`) is authored by the tooling's developers and flagged
|
|
336
|
+
`synthetic: true` everywhere, and `gates.js` refuses to count it toward any minimum. Do not quote a score from the synthetic suite
|
|
337
|
+
as engine accuracy.
|
|
338
|
+
|
|
339
|
+
- **`protocol.js` (QA-001).** `freezeProtocol` validates a draft and returns a deep-frozen protocol with `protocolHash` (a digest over
|
|
340
|
+
every semantic field). It pins engine/bundle digest, a clean measurement tree, tool and model versions, dataset licences, per-target
|
|
341
|
+
pre/post commits and tree digests, scope, matching policy, limits, thresholds and split membership. Thresholds are the PRD section 5
|
|
342
|
+
floor (`PREREGISTERED_THRESHOLDS`) and may be stricter, never looser or missing. `amendProtocol` yields a new versioned protocol with a
|
|
343
|
+
field diff and empty `comparableWith`; with ANY result bound to the protocol's hash it refuses (`POST_RESULT_CHANGE`), so a threshold,
|
|
344
|
+
matching rule or denominator cannot move after results are observed. `deriveSuccessorProtocol` is the only way forward and rejects a
|
|
345
|
+
sealed target a result already consumed (`SEALED_REUSE`). A target removed from the population must appear in `retired` with a reason
|
|
346
|
+
and stays in `originalTargetIds`. `assertBoundToProtocol` rejects a run scored under any other hash.
|
|
347
|
+
- **`grouping.js`.** Union-find over pair id, normalised upstream (a fork resolves to its parent), advisory ids, commits and template
|
|
348
|
+
fingerprints; `assignSplits` places whole groups by a salted hash, `splitStraddles` is the check the protocol validator runs.
|
|
349
|
+
- **`labels.js` (QA-002).** Defect labels need a root-cause id, affected commit, language, family, a reviewed location RANGE (a CWE
|
|
350
|
+
alone cannot be a label), evidence, and adjudication with at least two distinct reviewers who are not the proposer, recorded as
|
|
351
|
+
independent, plus non-model evidence. A candidate (advisory-only) label is valid data and never scoreable. Negatives are `safe-real`,
|
|
352
|
+
`patched` (post variant, paired to a defect) and `near-miss`. `matchFinding` matches by location plus family or CWE; CWE-only,
|
|
353
|
+
wrong-file, no-line and out-of-range each fail with a distinct reason. A finding with no line cannot be localised, so it never
|
|
354
|
+
matches (this is why the pattern-only layer scores zero on the synthetic suite: it reports no line, not a defect of the harness).
|
|
355
|
+
This module can validate that a label SAYS it was independently adjudicated, not that it was.
|
|
356
|
+
- **`custody.js` (QA-002.AC03).** The custodian (verifier domain, role `custodian`) is the only writer of the label store; read needs the
|
|
357
|
+
trust-domain `sealed-labels` read grant and the sealed hash to match. `protectedTermsFrom` / `guardPrompt` / `auditWorkspace` /
|
|
358
|
+
`stageWorkspace` keep advisory prose, evidence refs, benchmark names, fixed-source hints, answer-key file names, advisory ids and
|
|
359
|
+
symlinks out of anything the engine or a prompt sees; a leaking tree is quarantined (removed, recorded as an outcome, never scanned).
|
|
360
|
+
These are PATTERN checks over known terms, not proof against a paraphrase. Process-level read denial of the label directory is the
|
|
361
|
+
sandbox boundary's job (`runInBoundary({ labelDirs })`, which reports `blocked` where the host cannot prove the control).
|
|
362
|
+
- **`runner.js`, `scan-child.js` (QA-003).** `runEvaluation` records, per target and variant, status (`completed|timeout|error|unavailable|
|
|
363
|
+
quarantined`), findings, duration, cost (null when unmeasured, never zero), failure reason and input/output hashes. The engine runs in a
|
|
364
|
+
child process (killed by process group on timeout) over a staged copy, with a scrubbed environment. Run identity covers protocol,
|
|
365
|
+
engine, layer, provider, model, settings, cache, budget, seed, replicate, split and every target digest; labels are not part of it.
|
|
366
|
+
A tree that differs from the digest pinned in the protocol is refused. `runReplicates` checks deterministic reproduction and requires
|
|
367
|
+
at least three replicates for the stochastic (model-assisted) layer; `runAblations` runs `deterministic-only`, `deep-taint` and
|
|
368
|
+
`model-assisted` over identical targets and refuses incomparable configurations. `model-assisted` is `unavailable` without
|
|
369
|
+
`AGENTIC_SECURITY_LLM_ENDPOINT`, recorded as such.
|
|
370
|
+
- **`score.js`.** End-to-end scoring: a timeout, error, unavailable or quarantined known positive is a MISS. Conditional-on-completion
|
|
371
|
+
metrics are reported beside it, labelled, never instead of it. False positives are counted only on adjudicated negatives. Findings no
|
|
372
|
+
adjudicated label accounts for go to a review queue and `unscored` (with counts and reasons), never auto-FP. Targets with no
|
|
373
|
+
adjudicated label are `unlabeled-target`.
|
|
374
|
+
- **`gates.js`.** Judges a sealed-split score against the protocol's own thresholds. `pass` needs the population minimums (>=100 real,
|
|
375
|
+
adjudicated, non-synthetic sealed positives and negatives per core language, >=30 positives per family) AND a measured value over the
|
|
376
|
+
threshold; otherwise `insufficient-population` or `unmeasured`. A protocol failing its hash check, or a score from another protocol, is
|
|
377
|
+
`rejected`. The interval-lower-bound gate is `unmeasured` until the interval method is implemented (QA-004), so `overall` cannot be
|
|
378
|
+
`pass` in this build. `accuracy-scorecard.js` relays a gate report in an `evaluationGates` section (`summarizeEvaluationGates`),
|
|
379
|
+
forcing `unmeasured` for a synthetic or unmarked report; `scripts/scorecard.mjs` reads the gate report from the independent bench directory
|
|
380
|
+
when one has been written (none exists today).
|
|
381
|
+
|
|
382
|
+
Tests: `test/evaluation/{protocol,grouping,labels,custody,runner,gates-scorecard}.test.js` (`npm run test:evaluation`).
|
|
383
|
+
## Executable invariants, approval, scenarios and the business-state oracle: `invariants/` (X-401 to X-404)
|
|
384
|
+
|
|
385
|
+
Same posture as the verification directories above: consumed, not wired into the default scan, every output addition additive, and
|
|
386
|
+
everything off unless the **`invariant-scenarios`** feature (high-risk execution, operator-only, off by default) is enabled; running a
|
|
387
|
+
scenario also needs `verification-oracles`. Suite: `npm run test:invariants` (`test/invariants/`, executed tests skip loudly where the
|
|
388
|
+
trust boundary cannot run). Linux stays `unverified`: nothing here claims a Linux outcome. X-405 to X-408 (shrinking, repair
|
|
389
|
+
verification, coverage and export, the held-out ablation) are described in the section after this one.
|
|
390
|
+
|
|
391
|
+
- **`schema.js` + `expressions.js` (X-401).** The versioned invariant document: scope (an explicit application, entry, factory and
|
|
392
|
+
environment), tenants, actors, resources, allowed transitions, FORBIDDEN outcomes and an oracle binding. Five classes
|
|
393
|
+
(tenant-isolation, privilege-constraint, value-conservation, workflow-order, idempotency). Declarative only: forbidden outcomes are
|
|
394
|
+
expressions from a closed grammar (`EXPRESSIONS`), every string in them matches a conservative charset, so no field can carry code;
|
|
395
|
+
an op outside the grammar is rejected. Rejects ambiguous identities (duplicate actor/resource/transition/store key), dangling
|
|
396
|
+
references, a missing, unregistered or wrong-class oracle binding, and preserves author, revision and review state. The id is a
|
|
397
|
+
hash of the CONTENT (not the review), so an approval binds to exactly what was reviewed and an edit is a different contract.
|
|
398
|
+
`expressions.js` exists so the oracle adapter can validate expressions without importing the schema (the schema imports the
|
|
399
|
+
oracle registry; the registry imports the adapters).
|
|
400
|
+
- **`lifecycle.js` (X-402).** Inferred versus approved. `inferredInvariant` builds a PROPOSED skeleton (inferred, with source
|
|
401
|
+
evidence and an uncertainty, authored by a model or by code); miners reuse it: `specification-mining.js` (`mineInvariantProposals`),
|
|
402
|
+
`business-logic.js` (`mineWorkflowInvariants`), `logic-claims.js` (`invariantFromLogicClaim`) and the discovery lenses
|
|
403
|
+
(`discovery/lenses.js` `invariantProposalFor`, surfaced by `runDiscovery` as `invariantProposals` only when asked AND the feature is
|
|
404
|
+
on). The approval ledger is append-only, hash-chained and HMAC-signed under the per-install key (`integrity.js`): a transition
|
|
405
|
+
records who, why, from, to, the authority it rests on, and the previous record. Only a HUMAN named by an explicit local reviewer
|
|
406
|
+
policy (or verified by `fix/approver-registry.js`) can approve, reject or supersede; a model, code or the proposer cannot, and with
|
|
407
|
+
no policy nothing can be approved. `classifyViolation` calls a settled `confirmed` result an APPROVED violation (may gate) only
|
|
408
|
+
when the ledger verifies and says approved, else a CANDIDATE violation (advisory); a document that merely claims approval is a
|
|
409
|
+
candidate, and a result without a receipt `runOracle` issued is `unverified`. `violationReport` keeps the two groups apart.
|
|
410
|
+
Limits, stated: symmetric HMAC is tamper-evidence for the operator, not third-party non-repudiation; the reviewer identity is a
|
|
411
|
+
claim checked against the operator's list, not authenticated; dropping the LAST record is not detectable; persistence is the
|
|
412
|
+
caller's (the ledger is plain data).
|
|
413
|
+
- **`scenarios.js` (X-403).** `generateScenarios(invariant, { fixture, seed, bounds, config })`: five families (cross-tenant access,
|
|
414
|
+
privilege change, reordered workflow, duplicate request, concurrent operations), each with synthetic tenants and actors, a seeded
|
|
415
|
+
setup, a control sequence, an attack sequence, the forbidden outcomes and a cleanup. Bounded by actors, depth, requests,
|
|
416
|
+
scheduling (a parallel group is at most 4 steps with a SEEDED start order) and time (the budget becomes the run deadline); every
|
|
417
|
+
bound has a hard ceiling and an over-ceiling request is rejected, not clamped. Deterministic given the seed (no clock; ids are
|
|
418
|
+
hashes). Confined to contracts scoped to a `disposable-fixture` and to fixture files the caller supplies; a shared/production
|
|
419
|
+
scope, missing setup or a scenario that cannot be built is `unsupported` with its reason. `runScenario` builds a replay manifest
|
|
420
|
+
(`replay/replay.js`) so the environment, toolchain and oracle logic digest are pinned, then runs it. `run.js` (`verifyInvariant`)
|
|
421
|
+
is the one orchestration: generate, replay each scenario, classify against the ledger, report.
|
|
422
|
+
- **`business-state` oracle (X-404)** in `oracles/adapters.js` (class `business-state`, `requiresFeature: 'invariant-scenarios'`,
|
|
423
|
+
fixtures `test/fixtures/oracles/business-state/`, pinned like every adapter). The harness gives the application an in-memory
|
|
424
|
+
durable store and an effect recorder (`ctx.store`, `ctx.emit`), plays a control sequence and the attack sequence TWICE (a
|
|
425
|
+
disagreement is `inconclusive`, never a verdict), and logs every durable write and emitted effect. `state-assertions.js`
|
|
426
|
+
evaluates the forbidden outcomes VERIFIER-SIDE over that log after the tree is dead: durable writes, leaked content, totals,
|
|
427
|
+
state moves and repeated effects decide; the response status is recorded in the event sequence and never decides, so a handler
|
|
428
|
+
answering 403 after writing another tenant's record is a violation. A forbidden outcome reached by the control flow counts too.
|
|
429
|
+
`refuted` needs the control flow to have visibly worked (durable effect, no error, clean, cleaned up). The result carries a
|
|
430
|
+
bounded `report` (preconditions, sanitized snapshots with secret-looking fields redacted, the event sequence, per-assertion
|
|
431
|
+
results with stable `ievd:` evidence ids); the ids are record evidence and the report digest is bound into the receipt.
|
|
432
|
+
Two small additive changes to shared code: `oracle.js` supports an optional per-adapter `requiresFeature` and optional
|
|
433
|
+
`judged.report`/`judged.evidenceItems`; `conformance.js` enables an adapter's `requiresFeature` for its execution checks.
|
|
434
|
+
- **Limits, not hidden.** The application is a factory returning `(ctx, key, payload)` actions over an in-memory store: durable state
|
|
435
|
+
held in a real database, queue or file system is not observed. Concurrency is cooperative scheduling in one process (interleavings
|
|
436
|
+
at await points), not parallel execution. A clean result covers only the declared bounded scenario. A target that recognises the
|
|
437
|
+
oracle and forges its own log entries is not distinguishable from a real violation (the limit every adapter states).
|
|
438
|
+
- **`deployment-ablation.js` (X-308).** A frozen, SYNTHETIC deployment-context ablation: the same source deployed under an exploitable and a
|
|
439
|
+
non-exploitable boundary configuration (`test/fixtures/deployment-ablation/`, seven cases, fourteen instances, kubernetes and compose
|
|
440
|
+
deployments). `freezeAblationSet` hash-pins each source tree, each deployment tree, the labels and the graph arm's decision rule and binds
|
|
441
|
+
the cases into a QA-001 protocol (`synthetic: true`, never counted toward a real-population gate); `verifyFrozenSet` refuses any drift
|
|
442
|
+
before a measurement. `runPairedAblation` scores a source-only arm (the QA-003 child-process scan) and a graph-enabled arm (the shipped
|
|
443
|
+
`runBoundaries` path plus a frozen demotion rule: demote only on `blocked`, or `none-found` in a graph with no gap) on the same
|
|
444
|
+
instances and reports paired counts (false positives reduced, confirmed defects added, baseline confirmed defects lost, false positives
|
|
445
|
+
added), precision, recall, runtime and coverage (unresolved, stale and unbound reported apart). Uncertainty is a SIMPLE paired case
|
|
446
|
+
bootstrap, labelled as such: the grouped bootstrap of QA-004 is not in this base. `adapterValidation` marks an adapter validated only with
|
|
447
|
+
a positive and a negative fixture and no lost defect; `docs/guides/deployment-aware-support.md` may claim only those (a test compares).
|
|
448
|
+
CLI: `npm run evaluation -- deployment-ablation [--json] [--write-frozen]`. The cases, labels and rule are authored by the tooling's
|
|
449
|
+
developers and one case exists because an earlier rule lost a defect on it, so the set is a mechanism check, not an estimate.
|
|
450
|
+
|
|
451
|
+
Tests: `test/evaluation/{protocol,grouping,labels,custody,runner,gates-scorecard}.test.js` (`npm run test:evaluation`); the X-308 ablation is tested in `test/deployment/differentiation-ablation.test.js` (`npm run test:deployment`).
|
|
452
|
+
|
|
453
|
+
## Shrinking, repair verification, coverage and the held-out ablation (X-405 to X-408)
|
|
454
|
+
|
|
455
|
+
Same posture as above: consumed, not wired into the default scan, behind `invariant-scenarios`, every output addition additive. Suites:
|
|
456
|
+
`test/invariants/{scenario-shrinking,invariant-repair,business-coverage,invariant-ablation}.test.js`. Guide: `docs/guides/business-logic-invariants.md`.
|
|
457
|
+
|
|
458
|
+
- **`invariants/shrink.js` (X-405).** `shrinkScenario(scenario, { fixture, commit, config, budget })`: a greedy removal search (an attack step,
|
|
459
|
+
one member of a parallel group, a control step, a seed record), each candidate replayed through `runScenario` (so manifest, boundary,
|
|
460
|
+
receipt), kept only if it settles `confirmed` with every precondition held AND the same authoritative failing assertion (the first
|
|
461
|
+
violated outcome in the contract's order; identified by forbidden-outcome id and phase). Units a surviving step addresses, or that
|
|
462
|
+
carry an asserted marker, are guarded (recorded with a reason, never silently skipped). Budget: `maxRuns` and `timeBudgetMs`, each with
|
|
463
|
+
a hard ceiling and rejected, not clamped, above it; `minimal: true` only when a full pass kept nothing more. One confirming replay
|
|
464
|
+
sits outside the budget. `reproductionSignals` is a PATTERN check over the fixture text for clocks, randomness, timers, network and
|
|
465
|
+
package imports; `reproducibility.blockedBy` names concurrency (cooperative scheduling, two fresh runs disagreed), external
|
|
466
|
+
dependencies and prerequisites. `checkMinimization` re-derives from the two scenarios that the minimized one is a removal-subset
|
|
467
|
+
(invariant, fixture, actors, forbidden outcomes untouched; no step added; no sequence emptied; no referenced record dropped);
|
|
468
|
+
`verifyShrinkRecord` recomputes the digest, id and receipt links. `scenarios.js` gained `deriveScenario` (same pinned scenario, new
|
|
469
|
+
inputs, recomputed limits and id) and `exerciseOf`; `run.js` gained an optional `shrink` input and an `exercise` field per result.
|
|
470
|
+
- **`invariants/repair.js` (X-406).** `verifyInvariantRepair` composes `verifyPatchNegative` (business-state as the declared behaviour
|
|
471
|
+
check: `req.functional.oracleId` and `promotePatch`'s `functionalOracle` are the two small additive changes in `verification/`) with
|
|
472
|
+
preservation legs for every other APPROVED contract on the fixture (baseline and patched, a pre-existing violation is not blamed on
|
|
473
|
+
the patch). Needs the violated contract approved in a verifying ledger or nothing runs. Blockers carry codes from `REASON_CODES`;
|
|
474
|
+
`reasonCodeFor` maps every patch-negative failure code and sends an unknown one to `setup-failed`. A preservation failure after a
|
|
475
|
+
promotion appends a `rejected` repair record. `patch-negative.js` now also counts a failed business-state control flow
|
|
476
|
+
(`observed.preconditions`) as a broken revision. A verified repair builds a regression artifact (exact patch, invariant document and
|
|
477
|
+
approval evidence, scenario, one pinned manifest per leg with its expected outcome, secret-checked); `runRegressionArtifact`
|
|
478
|
+
re-executes it from the artifact alone, `validateRegressionArtifact` detects an edit without running. Receipts do not cross a
|
|
479
|
+
process; the artifact crosses by re-execution. The approval evidence in it is a digest and a transition id: checking the HMAC needs
|
|
480
|
+
the operator's key.
|
|
481
|
+
- **`invariants/coverage.js`, `export.js`, `project-input.js`, `cli.js` (X-407).** `businessCoverage` reports inventory by the VERIFIED
|
|
482
|
+
ledger, exercised actors/states/transitions from executed scenarios only, and gaps (`GAP_CODES`) that running more of the same cannot
|
|
483
|
+
remove; it always states it is a bounded sample. `invariantCoverageFields` is the one place a surface asks for the additive
|
|
484
|
+
`invariantCoverage` field (`report/index.js` JSON and CLI); flag off or nothing attached returns `{}`, pinned by
|
|
485
|
+
`test/fixtures/invariant-compat/`. `exportScenarios` builds the reproducible package (no fixture source; a scenario holding
|
|
486
|
+
secret-looking content is withheld by path; claims `exhaustive: false`); `verifyScenarioExport` detects edits. CLI:
|
|
487
|
+
`agentic-security invariants export|coverage|regress`; MCP: `invariant_scenario_export` (`mcp/invariant-tools.js`, read-only).
|
|
488
|
+
- **`evaluation/invariant-ablation.js` (X-408).** Three arms (source-only engine scan, inferred contract, approved contract) over a frozen
|
|
489
|
+
synthetic benchmark (`test/fixtures/invariant-benchmark/`, `manifest.json` + `pin.json`, loader fails closed), one denominator and one
|
|
490
|
+
budget, flags produced before any label is read, Wilson intervals, paired counts, unique findings, scenario cost, and per-class
|
|
491
|
+
claims that are `supported` only from executed evidence meeting the registered policy (nothing executed is `unmeasured`). Driver:
|
|
492
|
+
`npm run bench:invariant-ablation -- run|verify|pin` (`scripts/invariant-ablation.mjs`). SYNTHETIC: authored and labelled by the
|
|
493
|
+
developers, no independent adjudication; a figure from it is not engine accuracy and not a real-world benefit claim.
|
|
494
|
+
- **Limits, not hidden.** Same as the section above (in-memory store, cooperative concurrency, only declared outcomes). A shrunk
|
|
495
|
+
scenario is a smaller reproduction, not a root cause. A verified repair covers the declared scenario, workflows and the other
|
|
496
|
+
contracts' bounded scenarios only. Linux is not claimed anywhere here.
|
|
497
|
+
- **`interval.js`, `report.js` (QA-004).** `groupedBootstrap` is the registered `grouped-bootstrap-95` method: percentile bootstrap over GROUPS
|
|
498
|
+
(grouping.js), 2000 resamples, seed = first 32 bits of sha256(protocolHash + statistic name), `unmeasured` with the reason below 10 independent
|
|
499
|
+
groups. `wilsonInterval` covers the completion rate. `buildAccuracyReport` publishes raw counts first, then P/R/F1 by language and family,
|
|
500
|
+
micro and macro, completion with an interval, and every disclosed gap (unscored labels and findings, negatives whose scan failed). A stratum below
|
|
501
|
+
the protocol's floor is `unmeasured` with raw counts and a separate `descriptive` figure. `score.js` now carries `perTarget` tallies (what the
|
|
502
|
+
bootstrap resamples) and scores repeated alerts of one flaw as ONE false positive (`clusterAlerts`, `alertsCollapsed`); `alertBurden` reports the
|
|
503
|
+
raw alerts a reviewer reads beside the root causes, so deduplication never hides burden. `checkPopulationsDisjoint` refuses a target in two of
|
|
504
|
+
regression, development, sealed, operational.
|
|
505
|
+
- **`economics.js` (QA-004.AC03).** `accountCosts` keeps model, tool, cache (USD) and human-review (MINUTES) apart, treats an entry with no amount as
|
|
506
|
+
unmeasured (never zero; the ratios become lower bounds), and divides ALL machine spend, failed and duplicate attempts included, by DISTINCT confirmed
|
|
507
|
+
root causes and validated fixes. A confirmed outcome must name its root cause so a duplicate cannot be counted twice.
|
|
508
|
+
- **`why-missed.js` (QA-005).** `classifyMiss` names the earliest failed stage for a labelled DEVELOPMENT miss (scan-execution, parser-ir,
|
|
509
|
+
source-modelling, propagation, sink-modelling, filters, suppression, deduplication, attribution) or `unknown` with a reason. A candidate observed
|
|
510
|
+
downstream (a reported finding, a dedupe loser, a ledger drop) is checked first because it proves the earlier stages worked. Evidence comes from
|
|
511
|
+
`scan-child.js` in `diagnose` mode (the suppression ledger incl. guard-recognized drops with finding id, CWE, family and the dominating guard line;
|
|
512
|
+
dedupe and rejected-guard stage evidence behind `AGENTIC_SECURITY_STAGE_EVIDENCE=1`, diagnostic only; sources, sinks, IR lowering).
|
|
513
|
+
`collectDiagnostics` refuses any sealed target. `compareMisses` gives before and after stage counts, the recovered defects with the stage that lost
|
|
514
|
+
each, and names new false positives instead of netting them. `dev-cases.js` builds the development case suite (`test/fixtures/engine-mechanisms/`,
|
|
515
|
+
every label flagged synthetic) that `scripts/dev-recovery.mjs` runs against ANY engine checkout, so a before and after is a measurement.
|
|
516
|
+
- **`release-gate.js` (QA-007).** `evaluateReleaseGates` wraps `gates.js` (which now computes the per-language F1 lower bound from the grouped bootstrap
|
|
517
|
+
and adds the integrity gates `answer-key-leaks` and `denominator-intact`, and `no-regression-vs-baseline`) and decides whether a NEW accuracy claim is
|
|
518
|
+
allowed. The baseline comes only from a verified ledger: `appendSealedEvaluation` / `verifyLedger` (hash chain) / `signLedger` (the custodian signs the
|
|
519
|
+
head, so a rebuilt chain does not verify). `assessReleaseClaim` blocks a different detector digest (`detectorDigestOf`, evaluation tooling excluded) on
|
|
520
|
+
already-consumed sealed targets (`fresh-sealed-population-required`); old results are retained as evaluation-only. `signGateReport` signs an AGGREGATE
|
|
521
|
+
verdict (no per-case outcome) with a local Ed25519 key, trust basis `self-issued-local-key`, never independent certification.
|
|
522
|
+
- **`bakeoff.js` (QA-008.AC02).** The bake-off manifest, generic comparator slots (no tool is named anywhere), adapters that mark an unavailable slot
|
|
523
|
+
`not-evaluated` with a typed reason, a comparability check (identical workload digest and verification standard) and a claim guard: accuracy and
|
|
524
|
+
cost claims only between evaluated, comparable participants; `superiority` is always `none claimed`. Scoring is `comparison.js`, unchanged.
|
|
525
|
+
Docs: `docs/guides/bakeoff-recipe.md`; the offline reproduction is `scripts/public-reproduction.mjs` (`npm run reproduce:mini`).
|
|
526
|
+
|
|
527
|
+
Tests: `test/evaluation/{protocol,grouping,labels,custody,runner,gates-scorecard,reporting,why-missed,guard-dominance,engine-mechanisms,release-gate,publication}.test.js` (`npm run test:evaluation`; the two engine files are also in `test:dataflow`). Guides: `docs/guides/{evaluation-reporting,miniature-reproduction,bakeoff-recipe,engine-mechanism-evidence}.md`.
|
|
528
|
+
|
|
529
|
+
## Release assurance, portable evidence and resumable portfolios: `portfolio/` (X-701 to X-708)
|
|
530
|
+
|
|
531
|
+
Same posture as the sections above: consumed, not wired into the default scan. The one place it touches shipped output is the verdict
|
|
532
|
+
wording, and that is additive behind the `portfolio-assurance` feature (`assurance/config.js`, default off, so flag-off output is
|
|
533
|
+
byte-identical and no pinned fixture changed). **It builds machinery, not evidence**: no real release or portfolio exists, every
|
|
534
|
+
fixture is flagged `synthetic: true`, and nothing here claims real-world assurance. Linux enforcement stays `unverified`; no Linux
|
|
535
|
+
outcome is asserted. Suite: `npm run test:portfolio` (`test/portfolio/`).
|
|
536
|
+
|
|
537
|
+
- **`manifest.js` (X-701).** `buildManifest` / `validateManifest`: the release assurance manifest on the assurance schema kit and
|
|
538
|
+
`identity.js` (id prefix `ram`, allowlisted fields, no clock). Binds subject repository and commit, dependency revisions, build
|
|
539
|
+
artifact digests, scope, graph snapshot digest, invariant versions and verification receipts. Every mandatory check is in exactly one
|
|
540
|
+
of completed / incomplete / unsupported / waived (incomplete and unsupported name their gaps, a waiver names its approver and reason),
|
|
541
|
+
and the blocking policy (id, digest, severity) is part of the record. Validation rejects evidence not bound to a declared receipt,
|
|
542
|
+
a receipt whose commit is not the bound commit of its repository, a mandatory check listed nowhere, and coverage or `complete` that
|
|
543
|
+
disagrees with the lists. Residual risks and coverage are machine-readable fields.
|
|
544
|
+
- **`bundle.js` (X-702).** The portable bundle: an index file (bundle.json) plus `blobs/<sha256 hex>`. `exportBundle` writes the sanitized
|
|
545
|
+
findings (closed field set, snippets stay behind, secret shapes redacted, then refused if any survive), provenance, replay manifests,
|
|
546
|
+
toolchain identities and the cited receipts, index last. `verifyBundle` is the OFFLINE verifier (fs and crypto only, executes nothing
|
|
547
|
+
from the bundle): re-hashes every blob, rejects symlinks, oversize, bad names, missing roles, a modified index, a manifest that fails
|
|
548
|
+
validation and a cited receipt that is absent, and DISCLOSES replay prerequisites without attempting a replay. `importBundle`
|
|
549
|
+
verifies first, confines every destination, never overwrites. Limits are `BUNDLE_LIMITS`.
|
|
550
|
+
- **`signing.js` (X-703).** `signAssuranceClaim` is a signer-domain act (`sandbox/trust-domains.js`, default deny: worker, target and
|
|
551
|
+
verifier are refused before a key is touched). The key lives in its own `assurance-signing/` directory (reusing `ensureKeyPair`) and may
|
|
552
|
+
be required to differ from the scan evidence key. The signed claim names manifest id and digest, bundle digest, commit, blocking policy,
|
|
553
|
+
coverage, the bounded headline, signer id, key id and the assurance policy. `verifyAssuranceClaim` is offline against an explicit
|
|
554
|
+
trust-root policy (`buildTrustPolicy`): unknown root, revoked root, a basis this build cannot verify, a tampered claim, a tampered or
|
|
555
|
+
missing bundle and a claim that does not match the bundle's manifest are each typed failures. The only verifiable basis is
|
|
556
|
+
`self-issued-local-key`, `independentlyCertified` is always false and inside the signed bytes. Private key material is never in an
|
|
557
|
+
envelope, policy, error or output.
|
|
558
|
+
- **`wording.js` (X-703.AC03).** `assuranceStatement` renders "No blocking findings in completed supported checks", then scope, then
|
|
559
|
+
gaps, then the not-a-guarantee line. `report/index.js` `toShipVerdict` and `integrations/index.js` digests use it only when
|
|
560
|
+
`portfolioAssuranceEnabled` (option, else the assurance configuration); otherwise the old "Safe to deploy" strings are untouched.
|
|
561
|
+
- **`work-units.js` (X-704).** `planPortfolio` (authorized repositories only, exact commits, stable `wu:` ids), a seven-state machine with
|
|
562
|
+
lease expiry, retry count and a hash-chained attempt record per unit (rewriting an attempt fails `verifyStore`), idempotent lease /
|
|
563
|
+
start / complete / fail by attempt id (a duplicate completion verifies once; a stale attempt, such as a zombie after lease expiry,
|
|
564
|
+
is refused), `progressOf` counting only verified units, locked atomic persistence (`openStore`, `mutateStore`; a corrupt store is an
|
|
565
|
+
error, never a reset) and `runPortfolio` (bounded concurrency, an executor that throws fails its unit). `fleet.js` `runFleet` accepts
|
|
566
|
+
`portfolioVerified` (from `fullyVerifiedRepositories`) and skips those repositories like a resumed completed one.
|
|
567
|
+
- **`resume.js` (X-705).** Every verified result records one digest per dimension (code, policy, graph, invariant, oracle, toolchain;
|
|
568
|
+
`scan-checkpoint.js` `computeRunKey` / `computeGlobalKey` supply the code and toolchain digests). `planResume` reuses a unit only on a
|
|
569
|
+
full match; any other change `applyResume` invalidates: the old result moves to `stale` (inspectable with `inspectStale`), the unit
|
|
570
|
+
returns to pending in a new generation, `assessClaims` marks dependent claims stale. A graph-only change is narrowed by the drift
|
|
571
|
+
module (`lineage/deployment/drift.js` `planRescan`): a unit bound to hypotheses is reused only if the plan is complete and all its
|
|
572
|
+
hypotheses are in `skipped`; anything else fails closed.
|
|
573
|
+
|
|
574
|
+
Tests: `test/portfolio/{manifest,bundle,signing,work-units,resume}.test.js` (`npm run test:portfolio`).
|
|
575
|
+
## Calibrated model routing: `routing/` (X-601 to X-608)
|
|
576
|
+
- **`scheduler.js` (X-706).** Bounded scheduling over the store. `scheduleNext` admits and leases ONE unit in a single locked transaction
|
|
577
|
+
(`mutateStore`), after checking five limits at two levels (portfolio and per repository: wall time, provider spend, requests, storage,
|
|
578
|
+
plus concurrency) against what is used PLUS what in-flight attempts have reserved PLUS the unit's own estimate. A unit with no
|
|
579
|
+
estimate is not leased (unknown is never zero). A refusal is `at-capacity` (a wait) or `exhausted` (will not recover), reported per
|
|
580
|
+
unit and per scope, and a refusal changes nothing. An attempt that ends with no usage report is charged its whole reservation; a
|
|
581
|
+
reported overrun is recorded (`overruns`) and shrinks what remains. Wall time is also enforced while a unit runs (the executor's
|
|
582
|
+
`signal` aborts at the unit's wall estimate). Fairness: priority is a WEIGHT (integer 1..8, default 1), never an order; the next lease
|
|
583
|
+
goes to the admissible repository with the lowest `leasesGranted / weight` (ties: repository name, then unit id), so a 120-unit
|
|
584
|
+
repository cannot starve a 1-unit one, weight 8 against 1 is served about eight times as often with the light one still served early,
|
|
585
|
+
and a repository that was blocked is owed service when it returns. A blocked or over-budget repository is skipped with a reason, never
|
|
586
|
+
waited on; an unaffordable unit does not block a cheaper sibling. `routingBudgetFor` hands the routing policy (`routing/decide.js`) the
|
|
587
|
+
smaller of the portfolio and repository dollar remainders, less reservations; `chargeModelSpend` charges a routing decision at its
|
|
588
|
+
upper cost bound and refuses a blocked or unbounded decision. No provider is called here. `createCancelScope().cancel` takes a scope
|
|
589
|
+
(`{unitId}`, `{repository}` or `{all}`; an empty scope is refused, never read as cancel-all), records it in the ledger so a process
|
|
590
|
+
that did not receive the call still honours it, aborts in-process attempts (work started with `ctx.spawn` runs under
|
|
591
|
+
`sandbox/supervise.js`, which ends the whole process tree and reports survivors), releases a lease ONLY when the attempt settled with no
|
|
592
|
+
survivor, otherwise leaves it to expire (so no other worker can start the unit while a descendant may live) and never re-leases it.
|
|
593
|
+
Verified units and receipts are untouched; the report is `incomplete` with the reason. `heartbeat` renews the lease and records a
|
|
594
|
+
heartbeat in one transaction; `runScheduled` heartbeats every `HEARTBEAT.intervalMs` (5 s) while an attempt runs and takes an optional
|
|
595
|
+
`backend`, probed before every lease (an unavailable one stops the run as `blocked`). Limits: the driver's heartbeat proves the
|
|
596
|
+
controlling process is alive, not that an in-process executor is making progress (the wall bound handles that); tree termination is
|
|
597
|
+
verified on the macOS userspace backend only and Linux enforcement stays `unverified`.
|
|
598
|
+
- **`progress.js` (X-707).** ONE projection (`buildProgressView`) for the CLI, the MCP tool and the fleet summary, in the pattern of
|
|
599
|
+
`lineage/deployment/projection.js`. It keeps apart verified units (the only progress), blocked / failed / canceled / stale units with
|
|
600
|
+
reasons, per-repository coverage (fully-verified, partial, incomplete, not-started), per-limit budget (limit, used, reserved,
|
|
601
|
+
remaining; an unenforced limit says so), pending human review (blocked units plus `reviewItemsFromInvariantCoverage` and
|
|
602
|
+
`reviewItemsFromBoundaryContexts`), and every worker with its age, `live` or `stale` (stale after `HEARTBEAT.staleAfterMs`, 15 s;
|
|
603
|
+
silent workers are measured from their lease). `aggregateFindings` deduplicates by `stableId` ONLY, keeps every occurrence
|
|
604
|
+
(repository, environment, release, commit, file, line, severity) and lists affected releases separately; a finding with no stable id
|
|
605
|
+
is listed apart, never merged. `completion` states that a finished controller is not every unit verified and not any repository
|
|
606
|
+
passing (`passAssessment: 'not-implied'`). Additive behind `portfolio-assurance`: `attachPortfolioProgress` returns the SAME rollup
|
|
607
|
+
object when the feature is off, `renderFleetSummary` adds a PORTFOLIO clause only when the field is present, and
|
|
608
|
+
`test/fixtures/portfolio/pre-change-pins.json` (generated by `test/portfolio/make-pins.mjs` from code before the change) pins the
|
|
609
|
+
fleet rollup, summary and HTML. CLI `agentic-security portfolio progress`; MCP `portfolio_progress` (read-only, `mcp/portfolio-tools.js`).
|
|
610
|
+
- **`backend.js` (X-708).** The storage backend interface (`probe`, `storeFile`, `withExclusive`, fenced `acquireLease` / `renewLease` /
|
|
611
|
+
`releaseLease`) and a file-lock reference implementation. `createLocalBackend` makes its directory; `createSharedBackend` requires a
|
|
612
|
+
marker written by an explicit `initSharedBackend`, so an unmounted path, an empty mount point, a symlink or a damaged marker is a typed
|
|
613
|
+
`blocked` (`openBackend` returns `{ ok: false, state: 'blocked', code }` and no backend; there is deliberately no fallback argument,
|
|
614
|
+
and a blocked open creates nothing). A backend that disappears mid-run stops `runScheduled` as `blocked` without recreating the
|
|
615
|
+
directory or writing anywhere else. Lease semantics, each tested with two REAL processes: mutual exclusion, monotonic fence, expiry
|
|
616
|
+
takeover, a replaced holder refused on renew and release. NOT claimed: network-filesystem safety (`describe()` reports it
|
|
617
|
+
`unverified`; the operator must verify exclusive-create atomicity on that mount), cross-host clock agreement, or race-free stale-lock
|
|
618
|
+
recovery (narrow window, documented). `exportState` / `verifyStateExport` / `importState`: one self-checking, secret-checked file for an
|
|
619
|
+
air-gapped machine; import verifies first and never overwrites.
|
|
620
|
+
- **`retention.js` (X-708.AC01).** Retention by class (`replay-evidence`, `metadata`, `model-trace`, `secret`; defaults 365 / 730 / 30 / 0
|
|
621
|
+
days, ceilings 1095 / 1825 / 90 / 7, an over-ceiling setting is clamped and disclosed). Order: a required current receipt (named in
|
|
622
|
+
`currentReceiptIds`, or `requiredBy` a unit that is `verified` in the store NOW) is never deleted and an expired one is reported as
|
|
623
|
+
`expiredButRequired`; then a legal hold (validated and checked with `legal-hold.js`'s `isUnderHold`; by id, class or repository; a
|
|
624
|
+
malformed hold refuses the whole plan); then expiry. An unclassified or malformed record is kept and reported. `applyRetention` re-plans
|
|
625
|
+
against the live store, confines paths to the root (traversal, absolute paths, symlinks and directories refused), and logs each deletion
|
|
626
|
+
to a hash-chained log BEFORE removing the file (no log, no deletion; a broken log blocks every deletion). The log holds digests and sizes,
|
|
627
|
+
never content. CLI `agentic-security portfolio retention plan|apply|verify-log`.
|
|
628
|
+
|
|
629
|
+
Tests: `test/portfolio/{manifest,bundle,signing,work-units,resume,scheduler,progress,retention,backend}.test.js` (`npm run test:portfolio`).
|
|
630
|
+
`test/portfolio/proc-worker.mjs` is the child process the multi-process consistency tests spawn; it is not a test file.
|
|
631
|
+
## Calibrated model routing: `routing/` (X-601 to X-604)
|
|
632
|
+
|
|
633
|
+
Extends `model-routing.js`, `model-trust.js`, the provider catalog and cache economics; consumed, not wired into the scan or the interactive advisor. Everything is behind the **`model-routing`** feature (`assurance/config.js`, model-network risk, off by default, kill switch `AGENTIC_SECURITY_NO_MODEL_ROUTING`). With it off, `routeModelWithPolicy` returns exactly what `routeModelWithTrust` returns, and `test/routing/unchanged-when-off.test.js` recomputes the routing, trust, cache-economics, catalog and advisor outputs against `test/fixtures/routing/pre-change-pins.json` (generated from the code before any routing edit). Suite: `npm run test:routing`. **Machinery, not evidence:** no real routing outcome or adjudicated task exists in this repository, every population in the tests is SYNTHETIC (`test/helpers/routing-fixtures.js`), and the section 5 routing gate reads `unmeasured` (synthetic) or `insufficient-population`, never `pass`. No cost or quality improvement is claimed anywhere.
|
|
634
|
+
|
|
635
|
+
- **`outcomes.js` (X-601).** Routing task record (kind: discovery, triage, verification-planning, repair; language; vulnerability class; context tokens and bucket; required capabilities; data class), the stratum key `kind|language|class|bucket` and its coarser parents, and the outcome record. `deriveRoutingLabel` is the only way to a decided label: independent adjudication (a human or verifier service that is not the model or its provider, with the adjudication version) or trusted execution (a `vrec:` verification record). Provider agreement, self-reported confidence, accepted suggestions, model judgement and majority vote are refused (`NOT_A_TRUTH_LABEL`) and the outcome is recorded `unknown`. Outcome records keep every status (failed, timeout, cancelled, provider-error, partial, blocked) and unknown or delayed correctness; an unreported count is `null`, never 0. `createOutcomeLedger` has no remove or overwrite.
|
|
636
|
+
- **`economics.js` (X-602).** `priceBookFromCatalog` builds the versioned price book from `provider-catalog.js` (the catalog's dated snapshot stays the single price table). `costOf` splits input, cached input, output, retries and tool execution, and names price version, currency and billing basis. Unknown is `null` or a bounded `[lower, upper]` estimate, never 0 (an unpriced model, a subscription basis, an unreported token count, an unknown cache split, an unmeasured tool cost). A prediction goes in `predicted` and never fills `measured`. `cache-economics.js` (which prices Claude Code's own transcript) and `hooks/model-cost-advisor.js` are deliberately unchanged and pinned.
|
|
637
|
+
- **`adapter.js` (X-602.AC03).** `createProviderAdapter(...).invoke` has no HTTP client: it goes through `guardedModelCall` (feature gate, egress policy before the prompt exists, redaction, request cap, deadline, bounded retries, audit chain) with an INJECTED transport. Telemetry is built from an allowlist (provider, model, endpoint host only, status, a fixed failure code, token counts, cache state, latency, byte counts); it never carries the prompt, the response, an error message or a credential. Provider failures, partial responses, cancellation, timeouts and cache hits each yield an outcome record.
|
|
638
|
+
- **`calibration.js` (X-603).** `buildCalibration` fits per (model, version, stratum) quality on HELD-OUT outcomes with a time split, a model-version split (versions are never pooled) and group disjointness (a group on both sides is removed from the held-out side and counted). Uncertainty is `groupedBootstrap` from `evaluation/interval.js` (`unmeasured` below 10 groups). The label `reliable` needs at least 200 paired adjudicated tasks overall, 30 per stratum (`ROUTING_MINIMUMS`, a config cannot lower them), a measured interval and a stable development-versus-held-out rate; a synthetic population only ever gets `reliable-synthetic`. Otherwise the stratum carries `fallback-parent` (a reliable coarser stratum, named), `shifted` or `insufficient-evidence`, and an explicit fallback (`parent-stratum` or `baseline-model`). The artifact records the dataset hash, config hash, adjudication versions, counts and every exclusion by reason, and `verifyCalibration` rebuilds it byte for byte. `routingPopulationGate` is the population half of the PRD section 5 routing row only; the paired quality, cost and latency comparison is `promotion.js` (X-605).
|
|
639
|
+
- **`decide.js` (X-604).** `routeConstrained` applies availability, capability, privacy (the egress policy, or a capability-manifest egress check), context window, reliable and fresh quality evidence whose LOWER bound meets the minimum, interval width and budget BEFORE minimising measured cost or latency. Every candidate lists all of its failing constraints. When nothing is acceptable the result is `fallback` (the existing capability route, only if it passes capability, privacy, context and budget, with quality marked UNVERIFIED) or `blocked` with a code; a constraint is never relaxed to find a route. `routeModelWithPolicy` in `model-routing.js` is the entry point; a blocked decision returns `model: null, blocked: true`.
|
|
640
|
+
- **Limits, not hidden.** The provider catalog carries prices and cache models but not capabilities or context windows, so `candidateFromCatalog` takes them from the operator and an unknown window is rejected. Catalog prices are a dated snapshot. Cost bounds cover model tokens only. Telemetry redaction depends on the egress redaction patterns, which are pattern checks, not proof against every secret shape.
|
|
641
|
+
- **`shadow.js` (X-605.AC01).** `createShadowRecorder().shadow()` records what `routeConstrained` WOULD pick and returns the production route it was given, the same object. It holds no transport or adapter (a test pins that the source cannot call a provider), so no paid call is possible by construction. A disabled routing control records nothing.
|
|
642
|
+
- **`promotion.js` (X-605.AC02, AC03).** `freezeTaskSet` fixes the evaluation tasks by hash; `replayPaired` pairs each frozen task's baseline arm with the arm the shadow decision chose, from recorded outcomes. It issues a provider call only with BOTH an injected `invoke` and a bounded authorization (`authorizedBy`, finite `maxCalls`, `maxUsd`, `perCallUsdCeiling`, all under `REPLAY_LIMITS`); an `invoke` without them is refused (`UNBOUNDED_REPLAY`), an unknown cost is charged at the ceiling, and a replay that hits its bound is `truncated`. `evaluatePromotion` judges the PRD section 5 routing row (200 paired adjudicated overall, 30 per promoted stratum, quality difference lower bound above -0.02, median cost 20% lower or quality +0.05 at no higher median cost, p95 latency 1.2x) with the grouped bootstrap. Verdict precedence: `invalid` > `unmeasured` (synthetic) > `insufficient-population` > `fail` > `pass`. Invalidations: `cherry-picked` (pairs differ from the frozen set), `dropped-tasks` (no pair: no shadow record, missing or ambiguous outcome, bound reached), `unbounded-replay`, `truncated-replay`, `replay-edited`, `frozen-set-edited`. A failure status, a timeout, a blocked proposal or an unserved task is not-correct in the comparison and is reported, never dropped; an unadjudicated pair is counted apart and never imputed; an unknown cost or latency leaves the criterion unmet. The quality-gain path is stricter than the PRD wording (the lower bound must clear zero too).
|
|
643
|
+
- **`drift.js`, `canary.js` (X-606).** `snapshotRoutingBasis` records what a policy was promoted on; `assessDrift` compares it with the present (model version, price entry, schema, recent decided quality) and returns a documented `fallback` or `shadow` state; `applyInvalidations` derives a calibration whose affected estimates are `invalidated` (quality, version, schema) or lose their measured cost (pricing), so `routeConstrained` refuses them. A cost-invalidated model ranks behind one with a valid measured cost, by the existing ranking rule. `createCanary` is finite by construction (no limits, no canary): task count, spend budget, error rate, consecutive-error outage, incorrect rate; a trip restores the known-good policy and only appends receipts. `activate` needs a `pass` promotion verdict. `routeUnderDrift` is the state-aware entry point.
|
|
644
|
+
- **`feedback.js` (X-607).** `createFeedbackStore`: actors (human or verifier-service) registered with a key sign each record (HMAC); the first accepted label is the immutable original, a change is a correction citing the entry it corrects; an allowlist of fields is kept and the names of dropped fields only; unauthorized, unverifiable, tampered, duplicate and poisoned feedback is quarantined with a count and reason, and `applyToOutcomes` is what a calibration should be built from. `authorizePayload` / `sendToProvider` need the task policy AND the egress policy (or a capability manifest check) to allow source or evidence, and send only through the guarded adapter. Limit: a trusted-execution label cannot be recomputed from the outcome alone, so a flip of one is not detected here.
|
|
645
|
+
- **`receipts.js`, `control.js`, `report.js` (X-608).** `createReceiptLog` is a hash-linked log of every shadow decision, replay, promotion, drift, canary, rollback and feedback decision (`exportReceipts`, `verifyReceiptChain`; tamper evidence, not a signature). `resolveRoutingControl` (option, then `AGENTIC_SECURITY_ROUTING`, then default; a bad value fails closed to `disabled`) is honoured by `routeModelWithPolicy` (disabled keeps the capability route, `pin:<model>` selects that model). `buildRoutingReplayReport` / `renderRoutingReport` / `buildPolicyCard` restate the verdict with exact denominators, intervals, measured cost, cache states and latency percentiles, restrict claims to the strata and model versions the gate passed, and keep unsupported strata and unmet gates listed. Reproduce offline with `npm run reproduce:routing` (`scripts/routing-replay.mjs`, controls, `--fault`); guide `docs/guides/routing-replay.md`. Everything in the tests and the script is generated and `synthetic`; the gate reads `unmeasured`, never `pass`, on it, and no advantage is claimed.
|
|
646
|
+
|
|
316
647
|
## Execution-proof tiers (R2)
|
|
317
648
|
|
|
318
649
|
`proof-tier.js` + `execution-proof.js` add a fourth axis to a finding's
|
|
@@ -111,6 +111,55 @@ export function aggregateCorpus(detail) {
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Real-code evaluation gates (programme QA-001/QA-003). Takes the committed gate
|
|
116
|
+
* report written by `scripts/evaluation.mjs gates` (or nothing) and returns the
|
|
117
|
+
* section the scorecard publishes. It can only RELAY a status: a synthetic
|
|
118
|
+
* protocol is forced to `unmeasured`, and an absent report is `unmeasured` with
|
|
119
|
+
* the reason, never an empty pass. Pure.
|
|
120
|
+
*/
|
|
121
|
+
export function summarizeMeasurement(provenance, independent) {
|
|
122
|
+
const p = provenance || {};
|
|
123
|
+
const clean = p.worktreeClean === true;
|
|
124
|
+
const hist = independent && independent.engineVersion && independent.engineVersion !== p.engineVersion;
|
|
125
|
+
return {
|
|
126
|
+
engineVersion: p.engineVersion || null, commit: p.commit || null, bundleSha256: p.bundleSha256 || null,
|
|
127
|
+
cleanMeasurement: clean,
|
|
128
|
+
identity: clean
|
|
129
|
+
? 'the commit and bundle below identify exactly the tree that was measured'
|
|
130
|
+
: p.worktreeClean === false ? 'the worktree was dirty: the commit below does NOT identify what was measured, so this is not a clean measurement' : 'cleanliness of the measured tree was not recorded',
|
|
131
|
+
currentEngineIndependentAccuracy: 'unmeasured',
|
|
132
|
+
currentEngineIndependentReason: 'no adjudicated real-code population has been run against this engine; the independent record in this document, if any, describes an older engine',
|
|
133
|
+
historicalIndependentRecord: hist
|
|
134
|
+
? { engineVersion: independent.engineVersion, measuredAt: independent.measuredAt || null, usage: 'historical record only; not a measurement of the engine above and not combined with the corpus figures' }
|
|
135
|
+
: null,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function summarizeEvaluationGates(report) {
|
|
140
|
+
if (!report || typeof report !== 'object') {
|
|
141
|
+
return {
|
|
142
|
+
measuredThisRun: false, status: 'unmeasured', synthetic: null, gates: [],
|
|
143
|
+
reason: 'no frozen real-code evaluation protocol and no sealed adjudicated population exist yet; no real-code gate has been evaluated',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const gates = Array.isArray(report.gates) ? report.gates.map((g) => ({ id: g.id, status: g.status, threshold: g.threshold ?? null, measured: g.measured ?? null, reason: g.reason || null })) : [];
|
|
147
|
+
const synthetic = report.synthetic !== false;
|
|
148
|
+
const counts = {};
|
|
149
|
+
for (const g of gates) counts[g.status] = (counts[g.status] || 0) + 1;
|
|
150
|
+
return {
|
|
151
|
+
measuredThisRun: false,
|
|
152
|
+
source: 'bench/independent/evaluation-gates.json',
|
|
153
|
+
protocolHash: report.protocolHash || null,
|
|
154
|
+
synthetic,
|
|
155
|
+
// A synthetic protocol can exercise the machinery; it can never publish a gate result.
|
|
156
|
+
status: synthetic ? 'unmeasured' : (report.overall || 'unmeasured'),
|
|
157
|
+
reason: synthetic ? 'the recorded protocol is synthetic; its results are not real-code evidence' : (report.population?.reason || null),
|
|
158
|
+
gates: synthetic ? [] : gates,
|
|
159
|
+
gateCounts: synthetic ? {} : counts,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
114
163
|
/**
|
|
115
164
|
* Build the machine-readable scorecard model. `inputs`:
|
|
116
165
|
* provenance { engineVersion, bundleSha256, commit, nodeVersion, generatedAt,
|
|
@@ -227,6 +276,10 @@ export function buildScorecard(inputs) {
|
|
|
227
276
|
source: 'bench/independent/RESULT.json',
|
|
228
277
|
measuredAt: committed.independent.measuredAt || null,
|
|
229
278
|
engineVersion: committed.independent.engineVersion || null,
|
|
279
|
+
// QA-008.AC01: this record belongs to the engine it names. When that is not the engine this scorecard measured, it is a
|
|
280
|
+
// HISTORICAL record: kept, dated, labelled, and never combined with the curated-corpus figures into one accuracy claim.
|
|
281
|
+
historical: !!committed.independent.engineVersion && committed.independent.engineVersion !== (inputs.provenance || {}).engineVersion,
|
|
282
|
+
comparableToCurrentEngine: !!committed.independent.engineVersion && committed.independent.engineVersion === (inputs.provenance || {}).engineVersion,
|
|
230
283
|
population: committed.independent.population || null,
|
|
231
284
|
overall: committed.independent.overall || null,
|
|
232
285
|
wide: committed.independent.wide || null,
|
|
@@ -293,6 +346,10 @@ export function buildScorecard(inputs) {
|
|
|
293
346
|
// frameworks" from "this scorecard predates the metric" the same way
|
|
294
347
|
// every other section here distinguishes absence from zero.
|
|
295
348
|
complianceMappingCoverage: inputs.complianceMappingCoverage || [],
|
|
349
|
+
// QA-001/QA-003: the real-code gates registered in the evaluation protocol. Relayed, never computed here.
|
|
350
|
+
evaluationGates: summarizeEvaluationGates(inputs.evaluation),
|
|
351
|
+
// QA-008.AC01: what this scorecard measured, stated once. `clean` is what lets the commit and bundle identify the measured tree.
|
|
352
|
+
measurement: summarizeMeasurement(inputs.provenance, committed.independent),
|
|
296
353
|
};
|
|
297
354
|
}
|
|
298
355
|
|
|
@@ -344,6 +401,10 @@ export function renderScorecardMarkdown(m) {
|
|
|
344
401
|
if (p.worktreeClean !== undefined) {
|
|
345
402
|
L.push(`| Worktree at measurement time | ${p.worktreeClean ? 'clean' : 'DIRTY — the commit above does not fully describe what was measured'} |`);
|
|
346
403
|
}
|
|
404
|
+
if (m.measurement) {
|
|
405
|
+
L.push(`| Measurement identity | ${m.measurement.cleanMeasurement ? 'clean measurement: the commit and bundle above identify the measured tree' : 'NOT a clean measurement: the commit and bundle above do not identify the measured tree'} |`);
|
|
406
|
+
L.push('| Independent accuracy of THIS engine | unmeasured (no adjudicated real-code population has been run; see the historical record below, if present) |');
|
|
407
|
+
}
|
|
347
408
|
L.push(`| Node | ${p.nodeVersion || 'unknown'} |`);
|
|
348
409
|
L.push(`| Corpus entries | ${c.totalEntries} (${c.scoredEntries} scored) |`);
|
|
349
410
|
// FR-901: "Published results identify engine version, corpus version,
|
|
@@ -674,8 +735,17 @@ export function renderScorecardMarkdown(m) {
|
|
|
674
735
|
|
|
675
736
|
const ind = m.committedInputs.independent;
|
|
676
737
|
if (ind && ind.overall) {
|
|
677
|
-
|
|
738
|
+
const hist = ind.historical === true;
|
|
739
|
+
L.push(hist
|
|
740
|
+
? `## Independent evaluation population: historical record (engine ${ind.engineVersion}, ${ind.measuredAt || 'undated'})`
|
|
741
|
+
: '## Independent evaluation population — the number that matters');
|
|
678
742
|
L.push('');
|
|
743
|
+
if (hist) {
|
|
744
|
+
L.push(`**This is a historical record of engine ${ind.engineVersion}, measured ${ind.measuredAt || 'on an unrecorded date'}.** It is not a measurement of the`);
|
|
745
|
+
L.push(`engine this scorecard describes (${p.engineVersion || 'unknown'}), it is a different population from the curated corpus above, and the two are`);
|
|
746
|
+
L.push('never combined into one accuracy figure. The current engine has no independent figure: it is **unmeasured**.');
|
|
747
|
+
L.push('');
|
|
748
|
+
}
|
|
679
749
|
L.push('Everything above is a **regression net**: its fixtures and its labels are both');
|
|
680
750
|
L.push('written here, which is why its detection rate sits at the ceiling by');
|
|
681
751
|
L.push('construction. `bench/independent/` is the other instrument — real upstream code');
|
|
@@ -715,9 +785,14 @@ export function renderScorecardMarkdown(m) {
|
|
|
715
785
|
L.push('class at all, a question with a much easier yes. It is kept because it is the');
|
|
716
786
|
L.push('only way to tell whether a change moved the engine or moved the benchmark.');
|
|
717
787
|
L.push('');
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
788
|
+
if (hist) {
|
|
789
|
+
L.push(`This record is kept, dated and labelled because it is the last independent measurement the project made. It describes engine ${ind.engineVersion} only.`);
|
|
790
|
+
L.push('It does not describe the engine above, and the curated-corpus rates earlier in this document are a different population that must not be read beside it as one claim.');
|
|
791
|
+
} else {
|
|
792
|
+
L.push('Against ~100% on the curated corpus above. **That gap is the most useful number');
|
|
793
|
+
L.push('in this document**, and publishing it is the point of the exercise. The figure');
|
|
794
|
+
L.push('went DOWN when the benchmark was corrected, and is published that way.');
|
|
795
|
+
}
|
|
721
796
|
L.push('');
|
|
722
797
|
// FR-904 (assurance-hardening PRD): "rule authors cannot optimize
|
|
723
798
|
// against the full scored population." T0.7's held-out slice is a
|
|
@@ -771,6 +846,22 @@ export function renderScorecardMarkdown(m) {
|
|
|
771
846
|
}
|
|
772
847
|
}
|
|
773
848
|
}
|
|
849
|
+
const eg = m.evaluationGates || summarizeEvaluationGates(null);
|
|
850
|
+
L.push('## Real-code evaluation gates');
|
|
851
|
+
L.push('');
|
|
852
|
+
L.push(`Status: **${eg.status}**${eg.reason ? ` (${eg.reason})` : ''}.`);
|
|
853
|
+
L.push('');
|
|
854
|
+
L.push('The gates (per-language F1, micro and macro F1, pooled precision and recall, completion) are registered');
|
|
855
|
+
L.push('in a frozen protocol before any result is observed, and can read `pass` only on an independently');
|
|
856
|
+
L.push('adjudicated sealed population that meets the registered minimums. `insufficient-population` and');
|
|
857
|
+
L.push('`unmeasured` are honest blockers, not failures and not passes.');
|
|
858
|
+
if (eg.gates.length) {
|
|
859
|
+
L.push('');
|
|
860
|
+
L.push('| Gate | Status | Threshold | Measured |');
|
|
861
|
+
L.push('| --- | --- | --- | --- |');
|
|
862
|
+
for (const g of eg.gates) L.push(`| ${g.id} | ${g.status} | ${g.threshold ?? 'n/a'} | ${g.measured ?? 'n/a'} |`);
|
|
863
|
+
}
|
|
864
|
+
L.push('');
|
|
774
865
|
L.push('## Committed artifacts referenced (not re-run by this command)');
|
|
775
866
|
L.push('');
|
|
776
867
|
const cb = m.committedInputs.corpusBaseline;
|