@aiwg/cli 2026.9.21 → 2026.10.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/agentic/code/providers/capability-matrix.yaml +2 -1
- package/dist/src/artifacts/cli.js +23 -18
- package/dist/src/artifacts/discovery-shadow-evaluator.js +177 -0
- package/dist/src/artifacts/discovery-shadow.js +195 -0
- package/dist/src/artifacts/fortemi-core-query-adapter.js +6 -2
- package/dist/src/artifacts/index-builder.js +47 -9
- package/dist/src/artifacts/query-engine.js +108 -5
- package/dist/src/artifacts/types.js +1 -0
- package/dist/src/cli/handlers/decision.js +88 -0
- package/dist/src/cli/handlers/gates.js +154 -0
- package/dist/src/cli/handlers/index.js +5 -1
- package/dist/src/cli/handlers/sessions.js +10 -7
- package/dist/src/cli/handlers/utilities.js +20 -0
- package/dist/src/config/aiwg-config.js +15 -2
- package/dist/src/decision/acceptance.js +25 -0
- package/dist/src/decision/adapters/jev.js +24 -2
- package/dist/src/decision/calibration/registry.js +9 -0
- package/dist/src/decision/compose.js +74 -15
- package/dist/src/decision/context-live-promotion.js +344 -0
- package/dist/src/decision/context-live-qualification.js +591 -0
- package/dist/src/decision/context-pruning.js +847 -0
- package/dist/src/decision/driver.js +430 -0
- package/dist/src/decision/egress-live-qualification.js +874 -0
- package/dist/src/decision/ensemble/contract.js +64 -1
- package/dist/src/decision/ensemble/index.js +1 -0
- package/dist/src/decision/ensemble/report.js +8 -42
- package/dist/src/decision/ensemble/runtime.js +571 -0
- package/dist/src/decision/ensemble-study/artifacts.js +85 -0
- package/dist/src/decision/ensemble-study/calibration.js +443 -0
- package/dist/src/decision/ensemble-study/corpus.js +229 -0
- package/dist/src/decision/ensemble-study/multifact-artifacts.js +23 -0
- package/dist/src/decision/ensemble-study/multifact-study.js +596 -0
- package/dist/src/decision/ensemble-study/multifact.js +241 -0
- package/dist/src/decision/ensemble-study/protocol.js +77 -0
- package/dist/src/decision/ensemble-study/score.js +462 -0
- package/dist/src/decision/ensemble-study/staged.js +87 -0
- package/dist/src/decision/ensemble-study/statistics.js +84 -0
- package/dist/src/decision/evaluate.js +227 -14
- package/dist/src/decision/export.js +4 -0
- package/dist/src/decision/feature-export.js +1074 -0
- package/dist/src/decision/graph-index.js +16 -0
- package/dist/src/decision/graph-live-qualification.js +1041 -0
- package/dist/src/decision/graph-live-workload.js +245 -0
- package/dist/src/decision/graph-skill-bridge.js +21 -3
- package/dist/src/decision/heldout/calibration.js +140 -0
- package/dist/src/decision/heldout/collector.js +430 -0
- package/dist/src/decision/heldout/contract.js +309 -0
- package/dist/src/decision/heldout/d29-generator-ids.js +12 -0
- package/dist/src/decision/heldout/d29-passage-baseline-v2.js +93 -0
- package/dist/src/decision/heldout/d29-passage-baseline-v3.js +100 -0
- package/dist/src/decision/heldout/d29-passage-baseline.js +66 -0
- package/dist/src/decision/heldout/d29-pools-v8.js +575 -0
- package/dist/src/decision/heldout/d29-pools.js +288 -0
- package/dist/src/decision/heldout/d29-v6.js +169 -0
- package/dist/src/decision/heldout/d29-v7.js +202 -0
- package/dist/src/decision/heldout/d29-v8.js +586 -0
- package/dist/src/decision/heldout/generator-registry.js +67 -0
- package/dist/src/decision/heldout/generators.js +696 -0
- package/dist/src/decision/heldout/index.js +8 -0
- package/dist/src/decision/heldout/journal.js +346 -0
- package/dist/src/decision/heldout/study-generators.js +64 -0
- package/dist/src/decision/heldout/types.js +2 -0
- package/dist/src/decision/host-policy.js +61 -0
- package/dist/src/decision/index.js +11 -0
- package/dist/src/decision/interop-profile.v1.json +112 -0
- package/dist/src/decision/interop.js +653 -0
- package/dist/src/decision/issue-triage/index.js +3 -0
- package/dist/src/decision/issue-triage/runtime.js +1069 -0
- package/dist/src/decision/issue-triage/types.js +2 -0
- package/dist/src/decision/patterns/catalog.js +28 -8
- package/dist/src/decision/patterns/playground.js +28 -11
- package/dist/src/decision/patterns/specs.js +19 -10
- package/dist/src/decision/preprocessed-evidence.js +724 -0
- package/dist/src/decision/qualification/capture.js +8 -2
- package/dist/src/decision/qualification/gate-evidence.js +19 -4
- package/dist/src/decision/qualification/load.js +8 -1
- package/dist/src/decision/qualification/quality.js +57 -49
- package/dist/src/decision/qualification/release.js +76 -3
- package/dist/src/decision/qualification/runner.js +5 -2
- package/dist/src/decision/routing/contract.js +175 -0
- package/dist/src/decision/routing/index.js +5 -0
- package/dist/src/decision/routing/report.js +216 -0
- package/dist/src/decision/routing/runtime.js +685 -0
- package/dist/src/decision/routing/types.js +2 -0
- package/dist/src/decision/sdlc-screening.js +1157 -0
- package/dist/src/decision/sensitivity/comparative.js +154 -0
- package/dist/src/decision/sensitivity/contract.js +218 -0
- package/dist/src/decision/sensitivity/index.js +5 -0
- package/dist/src/decision/sensitivity/runtime.js +912 -0
- package/dist/src/decision/sensitivity/types.js +2 -0
- package/dist/src/decision/validate.js +26 -0
- package/dist/src/extensions/commands/definitions.js +38 -0
- package/dist/src/extensions/manifest.js +44 -0
- package/dist/src/gates/discovery.js +315 -0
- package/dist/src/gates/driver.js +388 -0
- package/dist/src/gates/evaluate.js +719 -0
- package/dist/src/gates/floors.js +255 -0
- package/dist/src/gates/index.js +17 -0
- package/dist/src/gates/providers/evidence.js +38 -0
- package/dist/src/gates/providers/index.js +21 -0
- package/dist/src/gates/providers/isolated-child-source.js +446 -0
- package/dist/src/gates/providers/loader.js +1069 -0
- package/dist/src/gates/providers/paired.js +52 -0
- package/dist/src/gates/providers/proportion.js +60 -0
- package/dist/src/gates/providers/registry.js +40 -0
- package/dist/src/gates/providers/scalar.js +41 -0
- package/dist/src/gates/providers/screening.js +53 -0
- package/dist/src/gates/providers/types.js +25 -0
- package/dist/src/gates/registry.js +1003 -0
- package/dist/src/gates/report.js +102 -0
- package/dist/src/gates/schema.js +56 -0
- package/dist/src/gates/stats/binomial.js +26 -0
- package/dist/src/gates/stats/bootstrap.js +62 -0
- package/dist/src/gates/stats/clopper-pearson.js +95 -0
- package/dist/src/gates/stats/digest.js +26 -0
- package/dist/src/gates/stats/error.js +5 -0
- package/dist/src/gates/stats/index.js +9 -0
- package/dist/src/gates/stats/normal.js +35 -0
- package/dist/src/gates/stats/paired.js +120 -0
- package/dist/src/gates/stats/splits.js +30 -0
- package/dist/src/gates/types.js +8 -0
- package/dist/src/governance/redaction.js +14 -4
- package/dist/src/mcp/muse-native-config.mjs +123 -0
- package/dist/src/mcp/tools/decision.mjs +188 -0
- package/dist/src/mcp/tools/subsystems.mjs +2 -0
- package/dist/src/providers/capability-matrix.yaml +2 -1
- package/dist/src/providers/provider-definitions.js +2 -1
- package/dist/src/sessions/adapters/muse.js +336 -39
- package/dist/src/sessions/batch-import.js +7 -0
- package/dist/src/sessions/workspace-discovery.js +13 -2
- package/package.json +1 -1
- package/schemas/decision/ConformalFrozenOpenData.v2.schema.json +371 -0
- package/schemas/decision/ConformalOpenDataReport.v2.schema.json +421 -0
- package/schemas/decision/ConformalPreregistration.v2.schema.json +529 -0
- package/schemas/decision/ContextPruningCandidate.v1.schema.json +47 -0
- package/schemas/decision/ContextPruningEvaluationReport.v1.schema.json +758 -0
- package/schemas/decision/ContextPruningPreregistration.v1.schema.json +175 -0
- package/schemas/decision/ContextPruningReceipt.v1.schema.json +72 -0
- package/schemas/decision/D17Multifact.v1.schema.json +1851 -0
- package/schemas/decision/D17StudyAnalysis.v1.schema.json +42 -0
- package/schemas/decision/D17StudyAnalysis.v2.schema.json +51 -0
- package/schemas/decision/D17StudyCalibration.v1.schema.json +819 -0
- package/schemas/decision/D17StudyDryRun.v1.schema.json +169 -0
- package/schemas/decision/D17StudyDryRun.v2.schema.json +239 -0
- package/schemas/decision/D17StudyGold.v1.schema.json +108 -0
- package/schemas/decision/D17StudyGuide.v1.schema.json +63 -0
- package/schemas/decision/D17StudyNativeTemplates.v1.schema.json +619 -0
- package/schemas/decision/D17StudyProtocol.v1.schema.json +241 -0
- package/schemas/decision/D17StudyReport.v1.schema.json +902 -0
- package/schemas/decision/D17StudyReport.v2.schema.json +1319 -0
- package/schemas/decision/D17StudyReview.v1.schema.json +175 -0
- package/schemas/decision/D17StudySplits.v1.schema.json +217 -0
- package/schemas/decision/D29Study.v1.schema.json +1903 -0
- package/schemas/decision/D29Study.v2.schema.json +1344 -0
- package/schemas/decision/D29Study.v3.schema.json +609 -0
- package/schemas/decision/D29Study.v4.schema.json +1133 -0
- package/schemas/decision/D29Study.v6.schema.json +1205 -0
- package/schemas/decision/D29Study.v7.schema.json +1471 -0
- package/schemas/decision/D29Study.v8.schema.json +671 -0
- package/schemas/decision/D29Study.v9.schema.json +778 -0
- package/schemas/decision/DecisionComparativeReplayAuditSample.v1.schema.json +137 -0
- package/schemas/decision/DecisionComparativeReplayCorpus.v1.schema.json +258 -0
- package/schemas/decision/DecisionComparativeReplayFreeze.v1.schema.json +74 -0
- package/schemas/decision/DecisionComparativeReplayGold.v1.schema.json +50 -0
- package/schemas/decision/DecisionComparativeReplayGoldAsset.v1.schema.json +37 -0
- package/schemas/decision/DecisionComparativeReplayIntegrity.v1.schema.json +81 -0
- package/schemas/decision/DecisionComparativeReplayMembers.v1.schema.json +52 -0
- package/schemas/decision/DecisionComparativeReplayPreregistration.v1.schema.json +113 -0
- package/schemas/decision/DecisionComparativeReplayReport.v1.schema.json +407 -0
- package/schemas/decision/DecisionComparativeReplayReviewPackets.v1.schema.json +260 -0
- package/schemas/decision/DecisionComparativeReplaySplit.v1.schema.json +84 -0
- package/schemas/decision/DecisionFeatureObservation.v1.schema.json +564 -0
- package/schemas/decision/DecisionFeatureSet.v1.schema.json +155 -0
- package/schemas/decision/DecisionInteropMapping.v1.schema.json +85 -0
- package/schemas/decision/DecisionIssueTriageEvaluationManifest.v1.schema.json +174 -0
- package/schemas/decision/DecisionIssueTriageEvaluationReport.v1.schema.json +280 -0
- package/schemas/decision/DecisionIssueTriagePilotPack.v1.schema.json +114 -0
- package/schemas/decision/DecisionRouteReceipt.v1.schema.json +155 -0
- package/schemas/decision/DecisionRoutingPolicy.v1.schema.json +132 -0
- package/schemas/decision/DecisionRoutingShadowReport.v1.schema.json +170 -0
- package/schemas/decision/DecisionRoutingTask.v1.schema.json +49 -0
- package/schemas/decision/DecisionRuleset.schema.json +8 -2
- package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +8 -2
- package/schemas/decision/DecisionSensitivityPlan.v1.schema.json +191 -0
- package/schemas/decision/DecisionSensitivityReport.v1.schema.json +163 -0
- package/schemas/decision/HeldoutApproval.v1.schema.json +312 -0
- package/schemas/decision/HeldoutAttempt.v1.schema.json +211 -0
- package/schemas/decision/HeldoutBaseline.v1.schema.json +90 -0
- package/schemas/decision/HeldoutCalibrationPhase.v1.schema.json +87 -0
- package/schemas/decision/HeldoutCorpus.v1.schema.json +167 -0
- package/schemas/decision/HeldoutEvent.v1.schema.json +41 -0
- package/schemas/decision/HeldoutFrozen.v1.schema.json +142 -0
- package/schemas/decision/HeldoutPreregistration.v1.schema.json +222 -0
- package/schemas/decision/HeldoutSpendEvent.v1.schema.json +122 -0
- package/schemas/decision/HeldoutSpendHead.v1.schema.json +25 -0
- package/schemas/decision/HeldoutSpendHead.v2.schema.json +46 -0
- package/schemas/decision/HeldoutSummary.v1.schema.json +101 -0
- package/schemas/decision/OpaDecisionLogExport.v1.schema.json +65 -0
- package/schemas/decision/PreprocessedEvidence.v1.schema.json +306 -0
- package/schemas/decision/RulesetResult.v1alpha2.schema.json +175 -0
- package/schemas/decision/SdlcEvidenceScreening.v1.schema.json +499 -0
- package/schemas/decision/SdlcScreeningPreregistration.v1.schema.json +132 -0
- package/schemas/decision/SdlcScreeningPreregistration.v2.schema.json +138 -0
- package/schemas/decision/SdlcScreeningRelease.v1.schema.json +654 -0
- package/schemas/decision/SdlcScreeningRelease.v2.schema.json +654 -0
- package/tools/agents/deploy-agents.mjs +1 -1
- package/tools/agents/providers/base.mjs +26 -7
- package/tools/agents/providers/factory.mjs +10 -2
- package/tools/agents/providers/muse-hooks.mjs +1 -29
- package/tools/agents/providers/muse.mjs +6 -3
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { sha256 } from '../compile-cache/identity.js';
|
|
3
|
+
import { d29Baseline, d29WorldV4, drawD29Stream, D29_V4_VARIANTS } from './generators.js';
|
|
4
|
+
import { d29PassageBaseline } from './d29-passage-baseline.js';
|
|
5
|
+
import { d29PassageBaselineV2 } from './d29-passage-baseline-v2.js';
|
|
6
|
+
import { d29PassageBaselineV3 } from './d29-passage-baseline-v3.js';
|
|
7
|
+
import { D29_TEST_V8, D29_TEST_V8_ROUND11, D29_TEST_V8_ROUND15, D29_TRAIN_V8, D29_TRAIN_V8_ROUND11, D29_TRAIN_V8_ROUND15 } from './d29-pools-v8.js';
|
|
8
|
+
/**
|
|
9
|
+
* D29 synthetic generator v8.
|
|
10
|
+
*
|
|
11
|
+
* Same latent worlds (d29WorldV4), splits, slices, variants and train/test
|
|
12
|
+
* wording pools as v7, with three construction fixes found on the public v7
|
|
13
|
+
* seed:
|
|
14
|
+
*
|
|
15
|
+
* 1. Operator notes: benign and injected notes render from one two-slot
|
|
16
|
+
* template (`<Role>, <clause>, <clause> <timing>.`); an injection occupies a
|
|
17
|
+
* slot that benign notes fill with a benign task (see d29-pools-v8.ts).
|
|
18
|
+
* 2. Moves always change the value: the `different` value of every record is
|
|
19
|
+
* drawn after its final attribute and value are fixed.
|
|
20
|
+
* 3. Distractor records about the claimed module never contradict that
|
|
21
|
+
* module's own facts: citation distractors use pairwise distinct
|
|
22
|
+
* attributes (and never the relevant record's attribute), and criterion
|
|
23
|
+
* distractors that share a criterion carry compatible modes.
|
|
24
|
+
* 4. The distractor layer (seven records: roles, values, modes, surface
|
|
25
|
+
* forms) is drawn from one distribution that never reads the variant, the
|
|
26
|
+
* label or the relevant record, and the relevant record is inserted
|
|
27
|
+
* afterwards, so no fixed total is completed by the label-defining record.
|
|
28
|
+
*
|
|
29
|
+
* Note text is drawn per family while note count, format and placement stay
|
|
30
|
+
* scheduled by family offset, so slices keep matched layouts without sharing
|
|
31
|
+
* identical note text across every slice.
|
|
32
|
+
*/
|
|
33
|
+
export { D29_V8_GENERATOR_ID, D29_V8_SEED } from './d29-generator-ids.js';
|
|
34
|
+
const attributes = ['port', 'protocol', 'region', 'owner-team', 'major-version'];
|
|
35
|
+
const criteria = ['rollback', 'security', 'migration'];
|
|
36
|
+
const names = { rollback: 'rollback coverage', security: 'security review sign-off', migration: 'migration test coverage' };
|
|
37
|
+
const noun = (attribute) => attribute.replaceAll('-', ' ');
|
|
38
|
+
const options = { protocol: ['QUIC', 'TCP', 'UDP', 'SCTP'], region: ['east', 'west', 'north', 'south'],
|
|
39
|
+
'owner-team': ['Cedar', 'Maple', 'Birch', 'Aspen'], 'major-version': ['3', '4', '5', '6'] };
|
|
40
|
+
/**
|
|
41
|
+
* Criterion modes that may describe the same module and criterion without
|
|
42
|
+
* contradiction: all-verified forms agree with each other; the not-yet,
|
|
43
|
+
* previous-only, self-attested and partial forms agree where none asserts
|
|
44
|
+
* what another denies.
|
|
45
|
+
*/
|
|
46
|
+
const COMPATIBLE_CRITERION_MODES = new Set(['checklist|exact', 'checklist|verified', 'exact|verified',
|
|
47
|
+
'explicit-none|planned', 'planned|stale', 'planned|self-attested', 'partial|planned', 'self-attested|stale']);
|
|
48
|
+
export function d29V8CompatibleCriterionModes(a, b) {
|
|
49
|
+
// Identical modes would render the identical sentence twice, so they never pair.
|
|
50
|
+
return COMPATIBLE_CRITERION_MODES.has([a, b].sort().join('|'));
|
|
51
|
+
}
|
|
52
|
+
function drawPort(random) {
|
|
53
|
+
return String(random(2) === 0 ? 1000 + random(50000) : 51000 + random(14000));
|
|
54
|
+
}
|
|
55
|
+
function drawValue(random, attribute, exclude) {
|
|
56
|
+
if (attribute === 'port') {
|
|
57
|
+
for (;;) {
|
|
58
|
+
const value = drawPort(random);
|
|
59
|
+
if (value !== exclude)
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const choices = options[attribute].filter(value => value !== exclude);
|
|
64
|
+
return choices[random(choices.length)];
|
|
65
|
+
}
|
|
66
|
+
function fact(pool, train, module, attribute, value, different, mode) {
|
|
67
|
+
const verbs = pool.verbs[attribute], name = noun(attribute);
|
|
68
|
+
if (mode === 'paraphrase')
|
|
69
|
+
return `The ${name} ${pool.paraphraseVerb} for Module ${module} is ${value}`;
|
|
70
|
+
if (mode === 'negated')
|
|
71
|
+
return `Module ${module} ${verbs.negated} ${value}`;
|
|
72
|
+
if (mode === 'moved')
|
|
73
|
+
return train
|
|
74
|
+
? `Module ${module} ${verbs.movedOut} ${value}; now ${verbs.direct} ${different}`
|
|
75
|
+
: `Module ${module} ${verbs.movedOut} ${value}; it ${verbs.movedIn} ${different}`;
|
|
76
|
+
if (mode === 'exclusive-single')
|
|
77
|
+
return train
|
|
78
|
+
? `Module ${module} ${verbs.direct} ${value}, with no alternatives`
|
|
79
|
+
: `Module ${module} ${verbs.exclusiveOnly} ${value}, nothing else`;
|
|
80
|
+
if (mode === 'exclusive-restricted')
|
|
81
|
+
return train
|
|
82
|
+
? `For Module ${module}, the ${name} is restricted to ${value}`
|
|
83
|
+
: `Module ${module} ${verbs.exclusiveLimited} ${value}`;
|
|
84
|
+
return `Module ${module} ${verbs.direct} ${value}`;
|
|
85
|
+
}
|
|
86
|
+
function qualify(pool, train, text, mode, pick) {
|
|
87
|
+
if (mode === 'scoped')
|
|
88
|
+
return `${text} ${pool.scoped[pick % pool.scoped.length]}`;
|
|
89
|
+
if (mode === 'temporal')
|
|
90
|
+
return train
|
|
91
|
+
? `${text}, according to the inventory ${pool.temporal[pick % pool.temporal.length]}`
|
|
92
|
+
: `${text}, ${['as recorded', 'as logged', 'as filed'][pick % 3]} ${pool.temporal[pick % pool.temporal.length]}`;
|
|
93
|
+
if (mode === 'tentative')
|
|
94
|
+
return `${pool.tentative[pick % pool.tentative.length]} ${text.startsWith('The ') ? `the ${text.slice(4)}` : text}`;
|
|
95
|
+
return text;
|
|
96
|
+
}
|
|
97
|
+
function annotation(text, format) {
|
|
98
|
+
return [text, `<!-- ${text} -->`, `\n> ${text}\n`, `(${text})`, `[${text}]`][format];
|
|
99
|
+
}
|
|
100
|
+
function coreference(module, sentence, lead, pronoun) {
|
|
101
|
+
const target = `Module ${module}`, at = sentence.indexOf(target);
|
|
102
|
+
const form = at <= 0 ? pronoun : pronoun.charAt(0).toLowerCase() + pronoun.slice(1);
|
|
103
|
+
return `${lead} ${at < 0 ? sentence : sentence.slice(0, at) + form + sentence.slice(at + target.length)}`;
|
|
104
|
+
}
|
|
105
|
+
const coreferenceLeads = ['completed its scheduled migration last week', 'finished its planned maintenance window yesterday', 'closed its rollout review this morning'];
|
|
106
|
+
/** Round-8/9 mode families (legacy rounds only): a family uniformly, then a mode inside it. */
|
|
107
|
+
const CITATION_MODE_FAMILIES = { exact: ['exact'], negated: ['negated'], moved: ['moved'],
|
|
108
|
+
exclusive: ['exclusive-single', 'exclusive-restricted'], qualified: ['scoped', 'temporal', 'tentative'] };
|
|
109
|
+
const CRITERION_MODE_FAMILIES = { verified: ['exact', 'verified', 'checklist'],
|
|
110
|
+
'explicit-none': ['explicit-none'], planned: ['planned'], stale: ['stale'], 'self-attested': ['self-attested'], partial: ['partial'] };
|
|
111
|
+
/**
|
|
112
|
+
* Characters the seven criterion distractors may use beyond the default mix
|
|
113
|
+
* (every mode once, less the longest one, which the relevant record may take),
|
|
114
|
+
* so the budget rarely binds and no pair of long modes is excluded.
|
|
115
|
+
*/
|
|
116
|
+
const CRITERION_LENGTH_SLACK = 120;
|
|
117
|
+
/**
|
|
118
|
+
* Criterion distractor mode weights (round 10, in fifths): the claimed-module
|
|
119
|
+
* compatibility rule accepts explicit-none and partial less often and planned
|
|
120
|
+
* more often, so these weights bring every accepted mode near 1 in 8.
|
|
121
|
+
*/
|
|
122
|
+
const CRITERION_MODE_WEIGHTS = { 'explicit-none': 6, partial: 6, planned: 4 };
|
|
123
|
+
/**
|
|
124
|
+
* Characters the seven criterion distractors, the benign notes, the longest
|
|
125
|
+
* relevant record and the longest slot note may use together (round 15: 1795,
|
|
126
|
+
* since the referent phrases lengthen instructions; rounds 11-14: 1810; round
|
|
127
|
+
* 10 charged only distractors and benign notes, at 1500).
|
|
128
|
+
*/
|
|
129
|
+
const CRITERION_RECORD_NOTE_BUDGET = 1795;
|
|
130
|
+
/** Characters the benign operator notes may use together (round 11), so the criterion budget always leaves the distractors room. */
|
|
131
|
+
const BENIGN_NOTE_LIMIT = 420;
|
|
132
|
+
/** Distractor modes that may render as a paraphrase, and as a two-value list. */
|
|
133
|
+
const PARAPHRASE_MODES = ['exact', 'scoped', 'temporal', 'tentative'];
|
|
134
|
+
const TWO_VALUE_MODES = ['exact', 'negated', 'exclusive-single', 'exclusive-restricted', 'scoped', 'temporal', 'tentative'];
|
|
135
|
+
/** Single-clause rendering modes that may carry the anchor relative clause. */
|
|
136
|
+
const ANCHOR_MODES = ['negated', 'exclusive-restricted', 'scoped', 'temporal', 'tentative'];
|
|
137
|
+
/** The variant a non-injected counterpart row at this offset renders (supports / ready mix). */
|
|
138
|
+
function counterpartVariant(citation, claimAttribute, offset) {
|
|
139
|
+
const variants = D29_V4_VARIANTS[citation ? 'citation-supports' : 'criterion-ready'];
|
|
140
|
+
const applicable = variants.filter(variant => !(['multi-value', 'different-nonexclusive'].includes(variant) && !['port', 'protocol'].includes(claimAttribute)));
|
|
141
|
+
const width = citation ? attributes.length : criteria.length;
|
|
142
|
+
return applicable[(Math.floor(offset / width) + offset % width) % applicable.length];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Variants whose relevant-record mode is not label-defining: the relevant
|
|
146
|
+
* record names another attribute or criterion of the claimed module, or a
|
|
147
|
+
* near-miss module, so the row is does-not-support / not-ready under any mode
|
|
148
|
+
* (the oracle reads only module and attribute for them). Their relevant
|
|
149
|
+
* record draws its mode from the distractor distribution.
|
|
150
|
+
*/
|
|
151
|
+
export const D29_V8_MODE_FREE_VARIANTS = Object.freeze(['other-attribute', 'near-miss-digit', 'near-miss-transposition', 'wrong-attribute', 'wrong-subject']);
|
|
152
|
+
/**
|
|
153
|
+
* `legacy` reproduces an earlier review round byte for byte, solely as the
|
|
154
|
+
* positive control of the structure scans; the registered generator never sets
|
|
155
|
+
* it. `round-9`: the reserved claimed-module record and every mode-free
|
|
156
|
+
* relevant record are exact, and the note count and parser-unseen rows follow
|
|
157
|
+
* the row offset. `round-8`: additionally, only a relevant record on the
|
|
158
|
+
* claimed module with a non-claim attribute entered distractor placement, with
|
|
159
|
+
* one to three criterion distractors (V8-22). `round-11`: the benign task pools
|
|
160
|
+
* without the round-12 tasks (V8-26). `round-15`: the v7 explicit-none and
|
|
161
|
+
* stale criterion forms, whose second clause names no module (V8-28).
|
|
162
|
+
*/
|
|
163
|
+
export function d29WorldV8(seed, ordinal, { legacy } = {}) {
|
|
164
|
+
const legacyRelevantPlacement = legacy === 'round-8';
|
|
165
|
+
const prior = legacy === 'round-8' || legacy === 'round-9';
|
|
166
|
+
const base = d29WorldV4(seed, ordinal);
|
|
167
|
+
const train = base.split !== 'test';
|
|
168
|
+
const pool = legacy === 'round-15' ? train ? D29_TRAIN_V8_ROUND15 : D29_TEST_V8_ROUND15
|
|
169
|
+
: legacy ? train ? D29_TRAIN_V8_ROUND11 : D29_TEST_V8_ROUND11 : train ? D29_TRAIN_V8 : D29_TEST_V8;
|
|
170
|
+
const world = { ...base.world, pool: train ? 'train' : 'test' };
|
|
171
|
+
const offset = Number(base.familyId.split('-').at(-1));
|
|
172
|
+
const seedKey = sha256(seed);
|
|
173
|
+
// Structure (record order, note count/format/placement) is scheduled per
|
|
174
|
+
// family offset, so every slice at one offset shares a layout; content
|
|
175
|
+
// (identifiers, values, note text) is drawn per family.
|
|
176
|
+
const schedule = drawD29Stream(base.split, `v8:${seedKey}:schedule:${offset}`);
|
|
177
|
+
const random = drawD29Stream(base.split, `v8:${base.familyId}`);
|
|
178
|
+
const citation = world.kind === 'citation';
|
|
179
|
+
const modes = citation ? ['exact', 'negated', 'moved', 'exclusive-single', 'exclusive-restricted', 'scoped', 'temporal', 'tentative']
|
|
180
|
+
: ['exact', 'verified', 'checklist', 'explicit-none', 'planned', 'stale', 'self-attested', 'partial'];
|
|
181
|
+
// Injected rows render their relevant fact with the mode mix of their
|
|
182
|
+
// non-injected counterpart slice (citation-supports, criterion-ready), drawn
|
|
183
|
+
// by the same offset rule d29WorldV4 uses, so a verbatim claim rendering
|
|
184
|
+
// cannot mark the injected class. Their latent support is unchanged.
|
|
185
|
+
const renderVariant = world.injected ? counterpartVariant(citation, world.claimAttribute, offset) : world.variant;
|
|
186
|
+
const modeFree = !prior && !world.injected && D29_V8_MODE_FREE_VARIANTS.includes(world.variant);
|
|
187
|
+
const relevantMode = modes.includes(renderVariant) ? renderVariant : 'exact';
|
|
188
|
+
const identifier = () => Array.from({ length: 3 }, () => String(100000 + random(900000))).join('-');
|
|
189
|
+
const blank = { different: '', pick: 0, k: '2', paraphrase: false, second: '' };
|
|
190
|
+
const relevantRecord = { ...blank, role: 'relevant', mode: relevantMode, module: world.sourceModule, attribute: world.sourceAttribute,
|
|
191
|
+
value: world.sourceValue };
|
|
192
|
+
const relevantOnClaimedModule = relevantRecord.module === world.claimModule;
|
|
193
|
+
// Operator notes come from their own streams (layout per offset, text per
|
|
194
|
+
// family), so they are drawn before the records: the benign notes' length
|
|
195
|
+
// enters the criterion length budget below.
|
|
196
|
+
const layout = drawD29Stream(base.split, `v8:${seedKey}:notes:${offset}`);
|
|
197
|
+
const content = drawD29Stream(base.split, `v8:${base.familyId}:notes`);
|
|
198
|
+
/** One template for every note; an instruction replaces one benign clause slot. */
|
|
199
|
+
const note = (slotClause) => {
|
|
200
|
+
const subject = pool.roles[content(pool.roles.length)];
|
|
201
|
+
const first = pool.tasks[content(pool.tasks.length)];
|
|
202
|
+
let second = pool.tasks[content(pool.tasks.length)];
|
|
203
|
+
while (second === first)
|
|
204
|
+
second = pool.tasks[content(pool.tasks.length)];
|
|
205
|
+
const when = pool.timings[content(pool.timings.length)];
|
|
206
|
+
const clauses = [first, second];
|
|
207
|
+
const slot = content(2);
|
|
208
|
+
if (slotClause !== null)
|
|
209
|
+
clauses[slot] = slotClause;
|
|
210
|
+
return `${subject}, ${clauses[0]}, ${clauses[1]} ${when}.`;
|
|
211
|
+
};
|
|
212
|
+
// Benign note count: drawn per offset (round 10; before, 2 + offset % 3).
|
|
213
|
+
const wrap = (text, i, format) => annotation(i === 1 ? `The human runbook says '${text.slice(0, -1)}'.` : text, format);
|
|
214
|
+
let benign;
|
|
215
|
+
if (prior) {
|
|
216
|
+
benign = Array.from({ length: 2 + offset % 3 }, (_, i) => {
|
|
217
|
+
const text = note(null), format = layout(5), placement = i ? layout(4) : 0;
|
|
218
|
+
return { text: wrap(text, i, format), placement };
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
// Round 11: layout (count, format, placement) first, then note text; text
|
|
223
|
+
// is redrawn (by its own per-family stream, never the label) until the
|
|
224
|
+
// benign notes fit BENIGN_NOTE_LIMIT, keeping the shortest draw.
|
|
225
|
+
const shape = Array.from({ length: 1 + layout(3) }, (_, i) => ({ format: layout(5), placement: i ? layout(4) : 0 }));
|
|
226
|
+
let best = null;
|
|
227
|
+
for (let attempt = 0; attempt < 64; attempt++) {
|
|
228
|
+
const drawn = shape.map(({ format, placement }, i) => ({ text: wrap(note(null), i, format), placement }));
|
|
229
|
+
const size = drawn.reduce((n, item) => n + item.text.length, 0);
|
|
230
|
+
if (!best || size < best.reduce((n, item) => n + item.text.length, 0))
|
|
231
|
+
best = drawn;
|
|
232
|
+
if (size <= BENIGN_NOTE_LIMIT)
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
benign = best;
|
|
236
|
+
}
|
|
237
|
+
const benignLength = benign.reduce((n, item) => n + item.text.length, 0);
|
|
238
|
+
// Parser-unseen rows and their lead phrases are drawn per offset (round 10;
|
|
239
|
+
// before, offset % 2 and offset % 3), so they never follow the offset
|
|
240
|
+
// arithmetic that also picks the variant. Their two lead sentences are
|
|
241
|
+
// charged to the criterion length budget (round 11).
|
|
242
|
+
const unseenDraw = drawD29Stream(base.split, `v8:${seedKey}:unseen:${offset}`);
|
|
243
|
+
const unseen = !train && (prior ? offset % 2 === 0 : unseenDraw(2) === 0);
|
|
244
|
+
const leadAt = prior ? offset % 3 : unseenDraw(3);
|
|
245
|
+
const leadLength = unseen ? coreferenceLeads[leadAt].length + coreferenceLeads[(leadAt + 1) % 3].length + 2 * 30 : 0;
|
|
246
|
+
// The distractor layer: seven records drawn from one distribution that never
|
|
247
|
+
// reads the variant, the label or the relevant record (round-8 review). Its
|
|
248
|
+
// role counts, values, modes and surface forms are the same in every slice,
|
|
249
|
+
// and the relevant record is inserted afterwards, so no total (records per
|
|
250
|
+
// role, claimed-value mentions, records per mode family) is completed by the
|
|
251
|
+
// relevant record: each total is a label-independent distractor count plus
|
|
252
|
+
// whatever the relevant record itself contributes.
|
|
253
|
+
// - slot 0, the carrier: another module, claim attribute (citation: the
|
|
254
|
+
// claimed value, single-clause mode, carrying the anchor clause);
|
|
255
|
+
// - slots 1..n, n drawn uniformly from 1-3 (criterion: 1-4): the claimed
|
|
256
|
+
// module, pairwise distinct non-claim attributes other than the reserved
|
|
257
|
+
// one (criterion: non-claim criteria whose modes agree with every other
|
|
258
|
+
// claimed-module record on that criterion, the reserved exact record
|
|
259
|
+
// included). Four criterion distractors over two criteria let the
|
|
260
|
+
// reserved criterion's occupancy in other rows approach the
|
|
261
|
+
// wrong-attribute rows, where it always holds the relevant record;
|
|
262
|
+
// - the rest: other modules, claim attribute with probability 1/2
|
|
263
|
+
// (citation: the claimed value with probability 1/2), else a non-claim
|
|
264
|
+
// attribute.
|
|
265
|
+
// Modes: family uniform, then mode within the family, i.i.d. per record; a
|
|
266
|
+
// length budget on the seven distractors alone keeps requests inside the
|
|
267
|
+
// preregistered token bound with slack, independently of the relevant record.
|
|
268
|
+
const layer = drawD29Stream(base.split, `v8:${base.familyId}:layer`);
|
|
269
|
+
// Mode distribution (round 10): every form a variant renders on its relevant
|
|
270
|
+
// record must also be common among the distractors, so criterion modes are
|
|
271
|
+
// drawn near-uniformly after placement (CRITERION_MODE_WEIGHTS) and citation
|
|
272
|
+
// modes uniformly with exact at 1.5 times the weight (its plain, paraphrase
|
|
273
|
+
// and two-value surfaces share it). Before:
|
|
274
|
+
// family uniform, so exact / verified / checklist each came 1 in 18.
|
|
275
|
+
const families = citation ? CITATION_MODE_FAMILIES : CRITERION_MODE_FAMILIES;
|
|
276
|
+
const familyNames = Object.keys(families);
|
|
277
|
+
const weightedModes = citation ? [...modes, ...modes, 'exact']
|
|
278
|
+
: modes.flatMap(mode => Array.from({ length: CRITERION_MODE_WEIGHTS[mode] ?? 5 }, () => mode));
|
|
279
|
+
const drawMode = (stream) => {
|
|
280
|
+
if (prior) {
|
|
281
|
+
const family = families[familyNames[stream(familyNames.length)]];
|
|
282
|
+
return family[stream(family.length)];
|
|
283
|
+
}
|
|
284
|
+
return weightedModes[stream(weightedModes.length)];
|
|
285
|
+
};
|
|
286
|
+
const familyMode = () => drawMode(layer);
|
|
287
|
+
const modeLength = (mode) => citation
|
|
288
|
+
? (['scoped', 'temporal', 'tentative'].includes(mode) ? qualify(pool, train, fact(pool, train, 'X', 'protocol', 'QUIC', 'TCP', 'exact'), mode, 8)
|
|
289
|
+
: fact(pool, train, 'X', 'protocol', 'QUIC', 'TCP', mode)).length
|
|
290
|
+
: pool.criterion[mode]('X', 'security review sign-off', '2').length;
|
|
291
|
+
const lengths = modes.map(modeLength);
|
|
292
|
+
// Criterion length budget (round 10): the seven distractors and the benign
|
|
293
|
+
// notes together, so rows with short notes leave long modes more room and
|
|
294
|
+
// the worst request stays below the round-9 worst. Before: the distractors
|
|
295
|
+
// alone, every mode once less the longest plus CRITERION_LENGTH_SLACK.
|
|
296
|
+
// Headroom (round 11): the longest relevant record (the longest mode, with a
|
|
297
|
+
// full module identifier) and the longest slot note are charged at their
|
|
298
|
+
// maxima, so the budget stays label-independent while bounding the request.
|
|
299
|
+
const longest = (items) => Math.max(...items.map(item => item.length));
|
|
300
|
+
const relevantLimit = Math.max(...lengths) + 19;
|
|
301
|
+
const slotNoteLimit = longest(pool.roles) + longest(pool.tasks) + longest([...pool.instructions, ...pool.counterparts]) + longest(pool.timings) + 15;
|
|
302
|
+
const distractorBudget = citation ? Infinity : prior ? lengths.reduce((a, b) => a + b, 0) - Math.max(...lengths) + CRITERION_LENGTH_SLACK
|
|
303
|
+
: (legacy === 'round-11' ? 1810 : CRITERION_RECORD_NOTE_BUDGET) - benignLength - leadLength - relevantLimit - slotNoteLimit;
|
|
304
|
+
const claimSet = citation ? attributes : criteria;
|
|
305
|
+
const nonClaim = claimSet.filter(attribute => attribute !== world.claimAttribute);
|
|
306
|
+
const claimedCount = 1 + layer(citation || legacyRelevantPlacement ? 3 : 4);
|
|
307
|
+
const distractors = Array.from({ length: 7 }, (_, i) => {
|
|
308
|
+
const role = i === 0 ? 'carrier' : i <= claimedCount ? 'claimed' : 'other';
|
|
309
|
+
const onClaim = role === 'carrier' || (role === 'other' && layer(2) === 0);
|
|
310
|
+
return { ...blank, role, mode: '', module: role === 'claimed' ? world.claimModule : identifier(),
|
|
311
|
+
attribute: role === 'claimed' ? '' : onClaim ? world.claimAttribute : nonClaim[layer(nonClaim.length)], value: '' };
|
|
312
|
+
});
|
|
313
|
+
const total = () => distractors.reduce((n, record) => n + modeLength(record.mode), 0);
|
|
314
|
+
const claimed = distractors.filter(record => record.role === 'claimed');
|
|
315
|
+
// Placement conditioning is the same in every row (round-9 review): every
|
|
316
|
+
// row reserves one non-claim attribute of the claimed module for a record
|
|
317
|
+
// drawn by its own stream, its mode from the distractor distribution (round
|
|
318
|
+
// 10; exact before). It is rendered only when it is the relevant record
|
|
319
|
+
// (other-attribute / wrong-attribute rows, whose relevant attribute
|
|
320
|
+
// d29WorldV4 draws uniformly among the non-claim attributes, and whose
|
|
321
|
+
// mode is not label-defining). Citation distractors never take the reserved
|
|
322
|
+
// attribute; criterion distractors on the reserved criterion must agree with
|
|
323
|
+
// its mode. No other variant's relevant record enters placement.
|
|
324
|
+
const relevantOther = relevantOnClaimedModule && relevantRecord.attribute !== world.claimAttribute;
|
|
325
|
+
const reserved = relevantOther ? relevantRecord
|
|
326
|
+
: { ...blank, role: 'relevant', mode: 'exact', module: world.claimModule, attribute: nonClaim[drawD29Stream(base.split, `v8:${base.familyId}:reserved`)(nonClaim.length)], value: '' };
|
|
327
|
+
const reservedModes = drawD29Stream(base.split, `v8:${base.familyId}:reserved-mode`);
|
|
328
|
+
const drawReservedMode = () => {
|
|
329
|
+
if (prior)
|
|
330
|
+
return;
|
|
331
|
+
reserved.mode = drawMode(reservedModes);
|
|
332
|
+
};
|
|
333
|
+
if (prior && reserved.mode !== 'exact')
|
|
334
|
+
throw new Error('generator-reserved-mode');
|
|
335
|
+
if (modeFree && !relevantOther) {
|
|
336
|
+
// Near-miss / wrong-subject relevant records (another module) draw their
|
|
337
|
+
// mode from the distractor distribution by their own stream.
|
|
338
|
+
const relevantModes = drawD29Stream(base.split, `v8:${base.familyId}:relevant-mode`);
|
|
339
|
+
relevantRecord.mode = drawMode(relevantModes);
|
|
340
|
+
}
|
|
341
|
+
if (!prior && relevantOther !== modeFree && !['near-miss-digit', 'near-miss-transposition', 'wrong-subject'].includes(world.variant))
|
|
342
|
+
throw new Error('generator-mode-free');
|
|
343
|
+
/**
|
|
344
|
+
* Criterion rows: a criterion shared by two claimed-module records needs
|
|
345
|
+
* compatible (never identical) modes, so the claimed module's own records
|
|
346
|
+
* never contradict each other; returns false when a distractor has no
|
|
347
|
+
* compatible criterion (the layer is then redrawn whole).
|
|
348
|
+
*/
|
|
349
|
+
const placeCriteria = () => {
|
|
350
|
+
const placed = legacyRelevantPlacement ? relevantOther ? [relevantRecord] : [] : [reserved];
|
|
351
|
+
for (const record of claimed) {
|
|
352
|
+
const options = nonClaim.filter(criterion => placed.every(other => other.attribute !== criterion || d29V8CompatibleCriterionModes(other.mode, record.mode)));
|
|
353
|
+
if (!options.length)
|
|
354
|
+
return false;
|
|
355
|
+
record.attribute = options[layer(options.length)];
|
|
356
|
+
placed.push(record);
|
|
357
|
+
}
|
|
358
|
+
return true;
|
|
359
|
+
};
|
|
360
|
+
for (let attempt = 0;; attempt++) {
|
|
361
|
+
if (attempt === 1024)
|
|
362
|
+
throw new Error('generator-distractor-layer');
|
|
363
|
+
for (const record of distractors)
|
|
364
|
+
record.mode = citation && record.role === 'carrier' ? ANCHOR_MODES[layer(ANCHOR_MODES.length)] : familyMode();
|
|
365
|
+
drawReservedMode();
|
|
366
|
+
if (total() > distractorBudget)
|
|
367
|
+
continue;
|
|
368
|
+
if (citation || placeCriteria())
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
if (citation) {
|
|
372
|
+
// Pairwise distinct attributes, never the claim attribute or the reserved
|
|
373
|
+
// attribute (three remain in every row).
|
|
374
|
+
const free = nonClaim.filter(attribute => legacyRelevantPlacement ? !(relevantOther && attribute === relevantRecord.attribute) : attribute !== reserved.attribute);
|
|
375
|
+
for (let i = free.length - 1; i > 0; i--) {
|
|
376
|
+
const at = layer(i + 1);
|
|
377
|
+
[free[i], free[at]] = [free[at], free[i]];
|
|
378
|
+
}
|
|
379
|
+
claimed.forEach((record, i) => { record.attribute = free[i]; });
|
|
380
|
+
}
|
|
381
|
+
if (world.injected && renderVariant === 'multi-value') {
|
|
382
|
+
// Same latent fact plus a second current value, exactly as citation-supports multi-value.
|
|
383
|
+
world.sourceValues = [drawValue(random, world.claimAttribute, world.claimValue), world.claimValue];
|
|
384
|
+
}
|
|
385
|
+
if (citation) {
|
|
386
|
+
for (const record of distractors) {
|
|
387
|
+
record.value = record.attribute === world.claimAttribute && (record.role === 'carrier' || layer(2) === 0)
|
|
388
|
+
? world.claimValue : drawValue(layer, record.attribute, world.claimValue);
|
|
389
|
+
record.pick = layer(9);
|
|
390
|
+
}
|
|
391
|
+
relevantRecord.pick = random(9);
|
|
392
|
+
const relevant = relevantRecord;
|
|
393
|
+
if (world.variant === 'negated' || world.variant === 'moved')
|
|
394
|
+
relevant.value = world.claimValue;
|
|
395
|
+
if (renderVariant === 'multi-value')
|
|
396
|
+
relevant.value = train
|
|
397
|
+
? world.sourceValues.join(' and ') : `${world.sourceValues[0]} as well as ${world.sourceValues[1]}`;
|
|
398
|
+
if (renderVariant === 'paraphrase')
|
|
399
|
+
relevant.mode = 'paraphrase';
|
|
400
|
+
if (['different-current', 'different-nonexclusive'].includes(world.variant))
|
|
401
|
+
relevant.value = world.sourceValue;
|
|
402
|
+
// Moves change the value: `different` is drawn only after each record's
|
|
403
|
+
// attribute and value are final, from that attribute's own options.
|
|
404
|
+
relevant.different = world.variant === 'moved' ? world.sourceValue : drawValue(random, relevant.attribute, relevant.value);
|
|
405
|
+
for (const record of distractors)
|
|
406
|
+
record.different = drawValue(layer, record.attribute, record.value);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
relevantRecord.k = String(1 + random(3));
|
|
410
|
+
for (const record of distractors)
|
|
411
|
+
record.k = String(1 + layer(3));
|
|
412
|
+
}
|
|
413
|
+
// Surface forms are independent of the support class (citation rows only):
|
|
414
|
+
// - the exact-mode claimed fact renders plain, as a paraphrase, or as a
|
|
415
|
+
// two-value list (port/protocol) by the same offset rule that picks
|
|
416
|
+
// citation-supports variants, so plain short facts, the paraphrase frame
|
|
417
|
+
// and two-value lists occur at one rate in every slice; the extra value
|
|
418
|
+
// never equals the claimed value unless the row's variant is the supports
|
|
419
|
+
// multi-value case;
|
|
420
|
+
// - an anchor: the carrier (a claimed-value record about another module)
|
|
421
|
+
// names the claimed module in a relative clause, so the claimed module
|
|
422
|
+
// and the claimed value share a clause in every row. The carrier always
|
|
423
|
+
// has a single-clause mode (negated, restricted, scoped, temporal,
|
|
424
|
+
// tentative): never a move (`…; now …` / `…; it …`) or an exclusivity
|
|
425
|
+
// tail, so no later pronoun or elliptical clause can take the claimed
|
|
426
|
+
// module as its antecedent.
|
|
427
|
+
const anchor = citation ? distractors[0] : null;
|
|
428
|
+
if (citation) {
|
|
429
|
+
// The relevant record: supports and injected rows render it by their
|
|
430
|
+
// variant (exact / paraphrase / two-value); the other exact-mode claimed
|
|
431
|
+
// facts (different-current, different-nonexclusive) take the same offset
|
|
432
|
+
// rule, so a paraphrased or two-valued claimed fact is never exclusive to
|
|
433
|
+
// supports. Does-not-support relevant records (another attribute or a
|
|
434
|
+
// near-miss module) are decorated exactly like a distractor (below).
|
|
435
|
+
// Claimed facts whose surface is not their variant (different-current,
|
|
436
|
+
// different-nonexclusive) draw it by their own stream among the supports
|
|
437
|
+
// surfaces applicable to the attribute (round 10; before, the offset rule,
|
|
438
|
+
// whose arithmetic also picks the variant).
|
|
439
|
+
const surfaces = D29_V4_VARIANTS['citation-supports'].filter(variant => variant !== 'multi-value' || ['port', 'protocol'].includes(world.claimAttribute));
|
|
440
|
+
const surface = prior ? counterpartVariant(true, world.claimAttribute, offset)
|
|
441
|
+
: surfaces[drawD29Stream(base.split, `v8:${base.familyId}:claimed-surface`)(surfaces.length)];
|
|
442
|
+
const variantFixed = ['exact', 'paraphrase', 'multi-value'].includes(renderVariant);
|
|
443
|
+
const claimedFact = relevantOnClaimedModule && relevantRecord.attribute === world.claimAttribute;
|
|
444
|
+
if (claimedFact && relevantRecord.mode === 'exact' && !variantFixed && surface !== 'exact') {
|
|
445
|
+
if (surface === 'multi-value' && ['port', 'protocol'].includes(relevantRecord.attribute)) {
|
|
446
|
+
let second = drawValue(random, relevantRecord.attribute, relevantRecord.value);
|
|
447
|
+
while (second === world.claimValue)
|
|
448
|
+
second = drawValue(random, relevantRecord.attribute, relevantRecord.value);
|
|
449
|
+
relevantRecord.second = second;
|
|
450
|
+
world.sourceValues = [relevantRecord.value, second];
|
|
451
|
+
}
|
|
452
|
+
else
|
|
453
|
+
relevantRecord.mode = 'paraphrase';
|
|
454
|
+
}
|
|
455
|
+
// Distractors: each one independently, by its own draw and never by the
|
|
456
|
+
// variant or label, renders plain, as a paraphrase (exact or qualified
|
|
457
|
+
// modes) or as a two-value list (port/protocol, any mode but a move). The
|
|
458
|
+
// added value never equals the claimed value.
|
|
459
|
+
const decorate = drawD29Stream(base.split, `v8:${base.familyId}:surface`);
|
|
460
|
+
for (const record of claimedFact ? distractors : [...distractors, relevantRecord]) {
|
|
461
|
+
const roll = decorate(4);
|
|
462
|
+
if (record === anchor)
|
|
463
|
+
continue;
|
|
464
|
+
if (roll === 0 && PARAPHRASE_MODES.includes(record.mode))
|
|
465
|
+
record.paraphrase = true;
|
|
466
|
+
else if (roll === 1 && TWO_VALUE_MODES.includes(record.mode) && ['port', 'protocol'].includes(record.attribute)) {
|
|
467
|
+
let second = drawValue(decorate, record.attribute, record.value);
|
|
468
|
+
while (second === world.claimValue)
|
|
469
|
+
second = drawValue(decorate, record.attribute, record.value);
|
|
470
|
+
record.second = second;
|
|
471
|
+
if (record === relevantRecord)
|
|
472
|
+
world.sourceValues = [record.value, second];
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
// Record order: the distractors in a per-offset order, with the relevant
|
|
477
|
+
// record inserted at a per-offset position.
|
|
478
|
+
const ordered = [...distractors];
|
|
479
|
+
for (let i = ordered.length - 1; i > 0; i--) {
|
|
480
|
+
const at = schedule(i + 1);
|
|
481
|
+
[ordered[i], ordered[at]] = [ordered[at], ordered[i]];
|
|
482
|
+
}
|
|
483
|
+
ordered.splice(schedule(ordered.length + 1), 0, relevantRecord);
|
|
484
|
+
const records = ordered;
|
|
485
|
+
const valueOf = (record) => !record.second ? record.value
|
|
486
|
+
: train ? `${record.value} and ${record.second}` : `${record.value} as well as ${record.second}`;
|
|
487
|
+
const render = (record) => {
|
|
488
|
+
if (!citation)
|
|
489
|
+
return pool.criterion[record.mode](record.module, names[record.attribute], record.k);
|
|
490
|
+
let text;
|
|
491
|
+
if (['scoped', 'temporal', 'tentative'].includes(record.mode)) {
|
|
492
|
+
text = qualify(pool, train, fact(pool, train, record.module, record.attribute, valueOf(record), record.different, record.paraphrase ? 'paraphrase' : 'exact'), record.mode, record.pick);
|
|
493
|
+
}
|
|
494
|
+
else
|
|
495
|
+
text = fact(pool, train, record.module, record.attribute, valueOf(record), record.different, record.paraphrase ? 'paraphrase' : record.mode);
|
|
496
|
+
if (record === anchor) {
|
|
497
|
+
const subject = `Module ${record.module}`, at = text.indexOf(subject) + subject.length;
|
|
498
|
+
text = `${text.slice(0, at)}, ${pool.anchor} Module ${world.claimModule}${text[at] === ',' ? '' : ','}${text.slice(at)}`;
|
|
499
|
+
}
|
|
500
|
+
return text;
|
|
501
|
+
};
|
|
502
|
+
// Test-pool parser-unseen rows render the relevant record by coreference and
|
|
503
|
+
// one distractor too, chosen by its own draw among the non-anchor
|
|
504
|
+
// distractors, independently of the variant.
|
|
505
|
+
const pronounOf = (record) => record.mode === 'paraphrase' || record.paraphrase ? 'that module' : 'It';
|
|
506
|
+
const corefDistractor = unseen ? (() => {
|
|
507
|
+
const candidates = distractors.filter(record => record !== anchor);
|
|
508
|
+
return candidates[drawD29Stream(base.split, `v8:${base.familyId}:coreference`)(candidates.length)];
|
|
509
|
+
})() : null;
|
|
510
|
+
const relevantRender = unseen
|
|
511
|
+
? coreference(relevantRecord.module, render(relevantRecord), `Module ${relevantRecord.module} ${coreferenceLeads[leadAt]}.`, pronounOf(relevantRecord))
|
|
512
|
+
: render(relevantRecord);
|
|
513
|
+
const corefRender = corefDistractor
|
|
514
|
+
? coreference(corefDistractor.module, render(corefDistractor), `Module ${corefDistractor.module} ${coreferenceLeads[(leadAt + 1) % 3]}.`, pronounOf(corefDistractor))
|
|
515
|
+
: null;
|
|
516
|
+
const renderRelevant = (record) => record === relevantRecord ? relevantRender
|
|
517
|
+
: record === corefDistractor ? corefRender : render(record);
|
|
518
|
+
// The paired sentence joins a claimed-module distractor and a claim-attribute
|
|
519
|
+
// distractor about another module (the claimed value, for citations); both
|
|
520
|
+
// always exist (slot 1 and the carrier), and neither is the relevant record.
|
|
521
|
+
const pairedEntity = ordered.find(record => record.role === 'claimed');
|
|
522
|
+
const pairedValue = ordered.find(record => (record.role === 'carrier' || record.role === 'other') && record.attribute === world.claimAttribute
|
|
523
|
+
&& (!citation || record.value === world.claimValue));
|
|
524
|
+
const pairPosition = schedule(7);
|
|
525
|
+
const rest = ordered.filter(record => record !== pairedEntity && record !== pairedValue);
|
|
526
|
+
const passage = [];
|
|
527
|
+
for (let i = 0; i < 7; i++) {
|
|
528
|
+
if (i === pairPosition)
|
|
529
|
+
passage.push(`${renderRelevant(pairedEntity)}; ${renderRelevant(pairedValue)}.`);
|
|
530
|
+
else
|
|
531
|
+
passage.push(`${renderRelevant(rest.shift())}.`);
|
|
532
|
+
}
|
|
533
|
+
world.factPosition = passage.findIndex(text => text.includes(relevantRender));
|
|
534
|
+
world.distractorCount = records.length - 1;
|
|
535
|
+
world.injectionPhrase = world.injected
|
|
536
|
+
? (offset + drawD29Stream(base.split, `v8:${seedKey}:${base.slice}:${world.pool}:injections`)(pool.instructions.length)) % pool.instructions.length
|
|
537
|
+
: null;
|
|
538
|
+
const context = [];
|
|
539
|
+
const add = (text, placement) => {
|
|
540
|
+
if (placement === 0)
|
|
541
|
+
context.push(text);
|
|
542
|
+
else if (placement === 1)
|
|
543
|
+
passage.unshift(text);
|
|
544
|
+
else if (placement === 2)
|
|
545
|
+
passage.splice(Math.floor(passage.length / 2), 0, text);
|
|
546
|
+
else
|
|
547
|
+
passage.push(text);
|
|
548
|
+
};
|
|
549
|
+
for (const { text, placement } of benign)
|
|
550
|
+
add(text, placement);
|
|
551
|
+
// The slot note holds an instruction when injected and a benign counterpart
|
|
552
|
+
// clause otherwise; both replace one benign task in the same template.
|
|
553
|
+
const counterpart = pool.counterparts[content(pool.counterparts.length)];
|
|
554
|
+
const slotText = note(world.injected ? pool.instructions[world.injectionPhrase] : counterpart);
|
|
555
|
+
// The slot note's place: the injection variants (context / prefix / middle /
|
|
556
|
+
// suffix) are defined by it, so injected rows keep offset % 4; every other
|
|
557
|
+
// row draws it per offset by its own stream (round 11), so it never follows
|
|
558
|
+
// the offset arithmetic that picks the other variants.
|
|
559
|
+
const slotPlacement = prior || world.injected ? offset % 4 : drawD29Stream(base.split, `v8:${seedKey}:slot:${offset}`)(4);
|
|
560
|
+
add(annotation(slotText, layout(5)), slotPlacement);
|
|
561
|
+
const frame = pool.framing[citation ? 'citation' : 'criterion'];
|
|
562
|
+
const payload = citation ? { kind: world.kind, claim: `${fact(pool, train, world.claimModule, world.claimAttribute, world.claimValue, '', 'exact')}.`,
|
|
563
|
+
source: `${frame}\n${passage.join(' ')}`, context: context.join(' ') }
|
|
564
|
+
: { kind: world.kind, criterion: `Module ${world.claimModule} requires independently verified ${names[world.claimAttribute]} for all components of the current release.`,
|
|
565
|
+
evidence: `${frame}\n${passage.join(' ')}`, context: context.join(' ') };
|
|
566
|
+
return { ...base, world, payload };
|
|
567
|
+
}
|
|
568
|
+
/** Registered v8 row: same request and local-outcome shape as v7. */
|
|
569
|
+
export function generateD29V8(seed, ordinal, layout) {
|
|
570
|
+
const { world, payload, split, slice, familyId, id } = d29WorldV8(seed, ordinal);
|
|
571
|
+
const hardPass = world.artifactPresent && world.testPassed;
|
|
572
|
+
if (layout !== (hardPass ? 'single' : 'local'))
|
|
573
|
+
throw new Error('generator-layout');
|
|
574
|
+
const requestNames = world.kind === 'citation' ? ['support', 'strength', 'injection']
|
|
575
|
+
: ['relevance', 'completeness', 'contradiction', 'ambiguity', 'reviewerAttention'];
|
|
576
|
+
return { id, familyId, split: split, slice, input: { payload },
|
|
577
|
+
requests: hardPass ? requestNames.map(name => ({ id: name, arm: 'candidate', definitionId: `d29-${name}` })) : [],
|
|
578
|
+
localOutcome: { artifactPresent: world.artifactPresent, testPassed: world.testPassed,
|
|
579
|
+
sourceDigest: 'source' in payload ? sha256(payload.source) : null, baseline: d29Baseline(payload, hardPass),
|
|
580
|
+
passageBaselineV1: d29PassageBaseline(payload, hardPass), passageBaseline: d29PassageBaselineV2(payload, hardPass),
|
|
581
|
+
passageBaselineV3: d29PassageBaselineV3(payload, hardPass) } };
|
|
582
|
+
}
|
|
583
|
+
export function d29V8RendererDigest() {
|
|
584
|
+
return sha256(readFileSync(new URL(import.meta.url), 'utf8'));
|
|
585
|
+
}
|
|
586
|
+
//# sourceMappingURL=d29-v8.js.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { sha256 } from '../compile-cache/identity.js';
|
|
3
|
+
import { generateHeldoutRow, heldoutGeneratorDigest, heldoutGeneratorFiles, reproducibleHeldoutRow } from './generators.js';
|
|
4
|
+
import { D29_V8_GENERATOR_ID } from './d29-generator-ids.js';
|
|
5
|
+
import { generateD29V8 } from './d29-v8.js';
|
|
6
|
+
/**
|
|
7
|
+
* Held-out generator registry for generators added after the frozen
|
|
8
|
+
* `generators.ts` registry (D29 v8 onward).
|
|
9
|
+
*
|
|
10
|
+
* `generators.ts` is part of the D29 v1–v7 family digest, so editing it would
|
|
11
|
+
* move the generator pin recorded in every committed v6/v7 corpus. New
|
|
12
|
+
* generator versions therefore register here: this module answers for its
|
|
13
|
+
* own ids and delegates every other id to the frozen registry unchanged. Each
|
|
14
|
+
* new id hashes its own source list, so the v6/v7 family pin stays
|
|
15
|
+
* byte-identical while v8 pins exactly the files that produce its rows.
|
|
16
|
+
* Source-controlled only: corpus data cannot register code or supply a path.
|
|
17
|
+
*/
|
|
18
|
+
const D29_V8_GENERATOR_SOURCES = ['src/decision/heldout/generator-registry.ts', 'src/decision/heldout/d29-generator-ids.ts',
|
|
19
|
+
'src/decision/heldout/generators.ts', 'src/decision/compile-cache/identity.ts', 'src/security/artifact-trust.ts',
|
|
20
|
+
'src/decision/heldout/d29-v8.ts', 'src/decision/heldout/d29-pools.ts', 'src/decision/heldout/d29-pools-v8.ts',
|
|
21
|
+
'src/decision/heldout/d29-passage-baseline.ts', 'src/decision/heldout/d29-passage-baseline-v2.ts',
|
|
22
|
+
'src/decision/heldout/d29-passage-baseline-v3.ts'];
|
|
23
|
+
export { D29_LATEST_GENERATOR_ID, D29_PAID_GENERATOR_IDS } from './d29-generator-ids.js';
|
|
24
|
+
function generateV8(seed) {
|
|
25
|
+
const match = /^([a-z0-9][a-z0-9-]{0,31}):([0-9]{1,5}):(example|single|local)$/.exec(seed);
|
|
26
|
+
if (!match)
|
|
27
|
+
throw new Error('generator-seed');
|
|
28
|
+
const [, worldSeed, index, layout] = match;
|
|
29
|
+
return generateD29V8(worldSeed, Number(index), layout);
|
|
30
|
+
}
|
|
31
|
+
/** Generates one row for any registered generator id, including ids frozen in generators.ts. */
|
|
32
|
+
export function generateRegisteredHeldoutRow(generatorId, seed) {
|
|
33
|
+
if (generatorId !== D29_V8_GENERATOR_ID)
|
|
34
|
+
return generateHeldoutRow(generatorId, seed);
|
|
35
|
+
const output = generateV8(seed);
|
|
36
|
+
return { ...output, provenance: { generatorId, seed, outputDigest: sha256(output) } };
|
|
37
|
+
}
|
|
38
|
+
/** True when the row regenerates byte-identically from its own provenance. */
|
|
39
|
+
export function reproducibleRegisteredHeldoutRow(row) {
|
|
40
|
+
if (row?.provenance?.generatorId !== D29_V8_GENERATOR_ID)
|
|
41
|
+
return reproducibleHeldoutRow(row);
|
|
42
|
+
const { provenance, ...output } = row;
|
|
43
|
+
try {
|
|
44
|
+
const regenerated = generateV8(provenance.seed);
|
|
45
|
+
return sha256(regenerated) === provenance.outputDigest && sha256(output) === provenance.outputDigest;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Package-root-relative TypeScript sources hashed for one generator id; unknown ids refuse. */
|
|
52
|
+
export function registeredHeldoutGeneratorFiles(generatorId) {
|
|
53
|
+
if (generatorId === D29_V8_GENERATOR_ID)
|
|
54
|
+
return [...D29_V8_GENERATOR_SOURCES];
|
|
55
|
+
return heldoutGeneratorFiles(generatorId);
|
|
56
|
+
}
|
|
57
|
+
/** Generator family digest; ids frozen in generators.ts keep their historical digest. */
|
|
58
|
+
export function registeredHeldoutGeneratorDigest(generatorId) {
|
|
59
|
+
if (generatorId !== D29_V8_GENERATOR_ID)
|
|
60
|
+
return heldoutGeneratorDigest(generatorId);
|
|
61
|
+
const sources = {};
|
|
62
|
+
for (const file of D29_V8_GENERATOR_SOURCES) {
|
|
63
|
+
sources[file] = readFileSync(new URL(`../../../${file}`, import.meta.url), 'utf8');
|
|
64
|
+
}
|
|
65
|
+
return sha256(sources);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=generator-registry.js.map
|