@event4u/agent-config 13.0.0 → 14.0.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +160 -333
- package/README.md +1 -1
- package/dist/agent-src/commands/evals/roadmap.json +8 -0
- package/dist/agent-src/commands/fix/ci.md +39 -1
- package/dist/agent-src/commands/pr/create.md +65 -4
- package/dist/agent-src/commands/roadmap/next.md +28 -5
- package/dist/agent-src/commands/work.md +16 -0
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +47 -0
- package/dist/agent-src/contexts/execution/contract-decision-sheet.md +94 -0
- package/dist/agent-src/contexts/execution/plan-confidence-gate.md +30 -0
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +21 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +55 -8
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +42 -0
- package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +29 -0
- package/dist/agent-src/rules/command-suggestion-policy.md +33 -0
- package/dist/agent-src/rules/design-review-after-ui-write.md +25 -8
- package/dist/agent-src/rules/ui-audit-gate.md +25 -8
- package/dist/agent-src/scripts/gate_execute.ts +395 -0
- package/dist/agent-src/scripts/resume_probe.ts +422 -0
- package/dist/agent-src/scripts/roadmap_gates.ts +138 -4
- package/dist/agent-src/scripts/update_roadmap_progress.ts +53 -2
- package/dist/agent-src/skills/ai-council/SKILL.md +61 -0
- package/dist/agent-src/skills/experiment-loop/SKILL.md +116 -0
- package/dist/agent-src/skills/experiment-loop/evals/output-schema.yml +27 -0
- package/dist/agent-src/skills/experiment-loop/evals/triggers.json +17 -0
- package/dist/agent-src/skills/experiment-loop/references/pivot-ladder.md +54 -0
- package/dist/agent-src/skills/experiment-loop/references/protocol.md +61 -0
- package/dist/agent-src/skills/experiment-loop/references/register-format.md +66 -0
- package/dist/agent-src/skills/roadmap-management/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/subagent-orchestration/schemas/team-review-status.json +8 -3
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +43 -3
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-N7FEST6D.js → chunk-7PZ4FSZB.js} +351 -174
- package/dist/cli-delegate/{chunk-3B72LSZL.js → chunk-F3YUF7SA.js} +1 -1
- package/dist/cli-delegate/cmd_conformance.js +224 -104
- package/dist/cli-delegate/cmd_doctor.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +40 -18
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +6 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +5 -4
- package/dist/hooks/dispatch.js +2018 -1320
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1652 -892
- package/dist/router.json +1 -1
- package/dist/scripts/condense.js +1 -1
- package/dist/scripts/condense.js.map +1 -1
- package/dist/ui/assets/{index-B0aMJnuz.js → index-C1pBRm1f.js} +2 -2
- package/dist/ui/assets/index-C1pBRm1f.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/src/agent-src/scripts/gate_execute.ts +395 -0
- package/src/agent-src/scripts/resume_probe.ts +422 -0
- package/src/agent-src/scripts/roadmap_gates.ts +138 -4
- package/src/agent-src/scripts/update_roadmap_progress.ts +53 -2
- package/src/config/ci-local-parity.yml +13 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +111 -5
- package/src/config/gate-violation-baselines.json +2 -2
- package/src/config/rule-activation-census.json +41 -0
- package/src/config/rule-stub-ceilings.json +52 -0
- package/src/scripts/_dispatch.bash +4 -0
- package/src/scripts/_lib/evaluator_contract.ts +159 -0
- package/src/scripts/_lib/gh_transient.ts +80 -0
- package/src/scripts/_lib/release_material.ts +16 -5
- package/src/scripts/_lib/review_independence.ts +111 -0
- package/src/scripts/_lib/session_register.ts +14 -0
- package/src/scripts/_lib/skill_catalogue.ts +20 -0
- package/src/scripts/_lib/skill_catalogue_series.ts +411 -0
- package/src/scripts/_lib/turn_end_refusals.ts +482 -0
- package/src/scripts/ai_council/_py_json.ts +96 -0
- package/src/scripts/ai_council/cli_call_budget.ts +357 -0
- package/src/scripts/ai_council/clients.ts +80 -143
- package/src/scripts/ai_council/config.ts +25 -11
- package/src/scripts/ai_council/probe_store.ts +196 -0
- package/src/scripts/ai_council/qualification.ts +425 -0
- package/src/scripts/ai_council/qualification_wiring.ts +243 -0
- package/src/scripts/ai_council/quorum_wiring.ts +211 -0
- package/src/scripts/ai_team/team_dispatch.ts +40 -6
- package/src/scripts/capture_skill_catalogue.ts +264 -4
- package/src/scripts/check_evaluator_schema.ts +249 -0
- package/src/scripts/check_pr_ci_current.ts +409 -0
- package/src/scripts/check_review_schema.ts +195 -0
- package/src/scripts/check_rule_activation_census.ts +451 -0
- package/src/scripts/check_rule_stub_ceiling.ts +1128 -0
- package/src/scripts/condense.ts +1 -1
- package/src/scripts/council_cli.ts +141 -191
- package/src/scripts/dispatch_r2_reviewer.ts +8 -1
- package/src/scripts/hook_manifest.yaml +72 -5
- package/src/scripts/hooks/concern_registry.ts +4 -0
- package/src/scripts/hooks/dispatch_hook.ts +43 -1
- package/src/scripts/hooks/interruption_ledger_hook.ts +256 -0
- package/src/scripts/hooks/ship_diff_volume_hook.ts +180 -0
- package/src/scripts/hooks/turn_end_gate_hook.ts +119 -26
- package/src/scripts/hooks_doctor.ts +112 -0
- package/src/scripts/interruption_report.ts +443 -0
- package/src/scripts/lint_evidence_artifacts.ts +359 -0
- package/src/scripts/lint_roadmap_blockers.ts +65 -0
- package/src/scripts/lint_roadmap_complexity.ts +7 -1
- package/src/scripts/release.ts +38 -53
- package/src/scripts/rule_activation_census.ts +292 -0
- package/src/scripts/schemas/evaluator-output.schema.json +60 -0
- package/src/scripts/schemas/review-findings.schema.json +31 -0
- package/src/scripts/self_review_gate.ts +6 -0
- package/src/scripts/session_register_hook.ts +30 -1
- package/src/scripts/sync_pr_branch.ts +271 -0
- package/src/scripts/test_council_qualification.ts +213 -0
- package/dist/ui/assets/index-B0aMJnuz.js.map +0 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transient-failure handling for `gh` calls in the release pipeline.
|
|
3
|
+
*
|
|
4
|
+
* `gh` does not retry GitHub's 5xx responses — including the pre-flight
|
|
5
|
+
* "does a PR already exist" GraphQL query it runs before every `pr create`.
|
|
6
|
+
* The release run therefore dies at whichever step happens to catch the
|
|
7
|
+
* outage, after the commit and the push already landed.
|
|
8
|
+
*
|
|
9
|
+
* Pure over strings and an injected executor so the classifier and the retry
|
|
10
|
+
* loop are exercised by the fixture tests without a network or a real sleep.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface GhRunResult {
|
|
14
|
+
returncode: number;
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Backoff between attempts. Length is the retry budget: three retries, ~23s. */
|
|
20
|
+
export const GH_RETRY_DELAYS_MS = [2_000, 6_000, 15_000] as const;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Retry-worthy failures only. A 4xx is the caller's fault and repeating it
|
|
24
|
+
* just delays the real message — the one exception is 429, which is the same
|
|
25
|
+
* wait-and-repeat shape as a 5xx.
|
|
26
|
+
*/
|
|
27
|
+
const _TRANSIENT_RE =
|
|
28
|
+
/HTTP 5\d\d|HTTP 429|No server is currently available|Something went wrong while executing your query|exceeded a secondary rate limit|\b(?:ETIMEDOUT|ECONNRESET|EAI_AGAIN)\b|TLS handshake timeout/;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Whether `stderr` describes a transient GitHub failure.
|
|
32
|
+
*
|
|
33
|
+
* Deliberately reads stderr ALONE. `gh` writes errors there, while stdout on a
|
|
34
|
+
* failing call can still carry payload the caller asked for (`pr view --json
|
|
35
|
+
* body`) — and a changelog body that happens to quote `HTTP 500` must not turn
|
|
36
|
+
* a permanent failure into three pointless retries.
|
|
37
|
+
*/
|
|
38
|
+
export function is_transient_gh_failure(stderr: string): boolean {
|
|
39
|
+
return _TRANSIENT_RE.test(stderr);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* `gh` argv rendered for an error line, long values elided.
|
|
44
|
+
*
|
|
45
|
+
* The release-PR body is passed as a single `--body` argument and runs to tens
|
|
46
|
+
* of thousands of characters. Rendering argv verbatim is what buried the real
|
|
47
|
+
* one-line `gh` error under a 22k dump on 14.0.0.
|
|
48
|
+
*/
|
|
49
|
+
export function gh_argv_label(args: readonly string[], max = 60): string {
|
|
50
|
+
return ['gh', ...args].map((a) => (a.length > max ? `${a.slice(0, max - 1)}…` : a)).join(' ');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Run `gh <args>` through `exec`, repeating while the failure is transient and
|
|
55
|
+
* the budget lasts. Returns the last result — classifying it is the caller's
|
|
56
|
+
* job, so a `check: false` probe keeps its own fallback behaviour.
|
|
57
|
+
*
|
|
58
|
+
* `sleep` and `notify` are injected rather than called directly so the loop is
|
|
59
|
+
* testable without a real 23-second wait, and so a caller running under a test
|
|
60
|
+
* exec-override can pass a no-op sleep while the delay sequence itself still
|
|
61
|
+
* runs and can be asserted.
|
|
62
|
+
*/
|
|
63
|
+
export function gh_retry(
|
|
64
|
+
args: readonly string[],
|
|
65
|
+
exec: (argv: readonly string[]) => GhRunResult,
|
|
66
|
+
opts: { sleep?: (ms: number) => void; notify?: (message: string) => void } = {},
|
|
67
|
+
): GhRunResult {
|
|
68
|
+
let result = exec(['gh', ...args]);
|
|
69
|
+
for (let i = 0; result.returncode !== 0 && i < GH_RETRY_DELAYS_MS.length; i++) {
|
|
70
|
+
if (!is_transient_gh_failure(result.stderr)) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
const ms = GH_RETRY_DELAYS_MS[i]!;
|
|
74
|
+
const first = result.stderr.trim().split('\n')[0] ?? '';
|
|
75
|
+
opts.notify?.(`↻ ${gh_argv_label(args, 24)} — transient GitHub failure, retry in ${ms / 1_000}s: ${first}`);
|
|
76
|
+
opts.sleep?.(ms);
|
|
77
|
+
result = exec(['gh', ...args]);
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
@@ -21,7 +21,18 @@ export interface ChangelogSection {
|
|
|
21
21
|
body: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Where a changelog section ends: the next release entry OR the next era
|
|
26
|
+
* banner. Era banners are level 1 and release entries level 2+ — the same
|
|
27
|
+
* invariant the release-validation `changelog-entry` gate relies on, and the
|
|
28
|
+
* one `lint_changelog_rollback` / `lint_breaking_changes_index` already encode
|
|
29
|
+
* as `/^##? /`. Without the era arm the newest entry runs to end-of-file after
|
|
30
|
+
* a split, which leaves it as the last `##` heading above archived banners.
|
|
31
|
+
*
|
|
32
|
+
* Exported because release.ts bounds the previous entry the same way, and two
|
|
33
|
+
* copies of a boundary is how one of them stays wrong.
|
|
34
|
+
*/
|
|
35
|
+
export const NEXT_SECTION_RE = /^(?:##\s+\[?\d+\.\d+\.\d+|# Era:)/m;
|
|
25
36
|
|
|
26
37
|
function _reEscape(s: string): string {
|
|
27
38
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -29,9 +40,9 @@ function _reEscape(s: string): string {
|
|
|
29
40
|
|
|
30
41
|
/**
|
|
31
42
|
* Extract the CHANGELOG section for `version` — heading plus body up to the
|
|
32
|
-
* next version heading (or end of file). Returns null when no
|
|
33
|
-
* `version` exists. Matches both heading shapes the generator
|
|
34
|
-
* (`## X.Y.Z (date)` and `## [X.Y.Z](compare-url) (date)`).
|
|
43
|
+
* next version heading or era banner (or end of file). Returns null when no
|
|
44
|
+
* heading for `version` exists. Matches both heading shapes the generator
|
|
45
|
+
* emits (`## X.Y.Z (date)` and `## [X.Y.Z](compare-url) (date)`).
|
|
35
46
|
*/
|
|
36
47
|
export function extract_changelog_section(text: string, version: string): ChangelogSection | null {
|
|
37
48
|
const headingRe = new RegExp(`^##\\s+\\[?${_reEscape(version)}\\b.*$`, 'm');
|
|
@@ -41,7 +52,7 @@ export function extract_changelog_section(text: string, version: string): Change
|
|
|
41
52
|
}
|
|
42
53
|
const bodyStart = m.index + m[0].length;
|
|
43
54
|
const rest = text.slice(bodyStart);
|
|
44
|
-
const next =
|
|
55
|
+
const next = NEXT_SECTION_RE.exec(rest);
|
|
45
56
|
const sectionEnd = bodyStart + (next ? next.index : rest.length);
|
|
46
57
|
const body = text
|
|
47
58
|
.slice(bodyStart, sectionEnd)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review independence — a recorded property of a review, never a promise.
|
|
3
|
+
*
|
|
4
|
+
* The failure this closes: a review whose members all share a model family
|
|
5
|
+
* produces an artifact byte-indistinguishable from one backed by independent
|
|
6
|
+
* models, so a consumer reads "no findings" as acceptance either way. The
|
|
7
|
+
* self-review gate is the live instance — it calls ONE Anthropic client, and
|
|
8
|
+
* the ledger it writes says nothing about that.
|
|
9
|
+
*
|
|
10
|
+
* Derived, never set by hand: `acceptance_status` follows from
|
|
11
|
+
* `review_independence`, so a same-family set carrying `accepted` is
|
|
12
|
+
* unrepresentable rather than merely forbidden. Two fields a producer can set
|
|
13
|
+
* inconsistently would reintroduce the ambiguity the pair exists to remove.
|
|
14
|
+
*
|
|
15
|
+
* Evidence: agents/evidence/eval-findings/metric-loop-s03.md.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** The five providers `ai_council/config.ts::_VALID_PROVIDERS` admits. */
|
|
19
|
+
const PROVIDER_FAMILY: Readonly<Record<string, string>> = {
|
|
20
|
+
anthropic: 'anthropic',
|
|
21
|
+
openai: 'openai',
|
|
22
|
+
gemini: 'google',
|
|
23
|
+
xai: 'xai',
|
|
24
|
+
perplexity: 'perplexity',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type ReviewIndependence = 'cross-family' | 'same-family' | 'single-member' | 'unknown';
|
|
28
|
+
export type AcceptanceStatus = 'accepted' | 'provisional';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* How much INDEPENDENT evidence backs the verdict — deliberately orthogonal to
|
|
32
|
+
* how much effort the reviewer spent.
|
|
33
|
+
*
|
|
34
|
+
* Collapsing the two is what lets a long, careful, single-model pass read as
|
|
35
|
+
* acceptance: effort is visible in the artifact (findings, detail, rationale)
|
|
36
|
+
* and independence is not, so a reader with only one axis substitutes the one
|
|
37
|
+
* they can see. A five-hour same-family review is `single-pass`, and a terse
|
|
38
|
+
* two-family one is `independent`. That inversion is the point.
|
|
39
|
+
*/
|
|
40
|
+
export type Assurance = 'unreviewed' | 'single-pass' | 'independent';
|
|
41
|
+
|
|
42
|
+
export function reviewIndependence(memberNames: readonly string[]): ReviewIndependence {
|
|
43
|
+
const present = memberNames.map((n) => n.trim()).filter((n) => n.length > 0);
|
|
44
|
+
if (present.length === 0) return 'unknown';
|
|
45
|
+
if (present.length === 1) return 'single-member';
|
|
46
|
+
const families = new Set(present.map((n) => PROVIDER_FAMILY[n] ?? `unknown:${n}`));
|
|
47
|
+
return families.size > 1 ? 'cross-family' : 'same-family';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Only a cross-family set may be described as acceptance.
|
|
52
|
+
*
|
|
53
|
+
* `unknown` maps to `provisional`, not to a third value: an absent member set
|
|
54
|
+
* is not evidence of independence, and the safe direction on an integrity field
|
|
55
|
+
* is the weaker claim.
|
|
56
|
+
*/
|
|
57
|
+
export function acceptanceStatus(independence: ReviewIndependence): AcceptanceStatus {
|
|
58
|
+
return independence === 'cross-family' ? 'accepted' : 'provisional';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function assuranceFor(independence: ReviewIndependence): Assurance {
|
|
62
|
+
if (independence === 'cross-family') return 'independent';
|
|
63
|
+
if (independence === 'unknown') return 'unreviewed';
|
|
64
|
+
return 'single-pass';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface IndependenceFields {
|
|
68
|
+
readonly review_independence: ReviewIndependence;
|
|
69
|
+
readonly acceptance_status: AcceptanceStatus;
|
|
70
|
+
readonly assurance: Assurance;
|
|
71
|
+
readonly reviewers: readonly string[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** The three fields plus the member set they were derived from. */
|
|
75
|
+
export function independenceFields(memberNames: readonly string[]): IndependenceFields {
|
|
76
|
+
const independence = reviewIndependence(memberNames);
|
|
77
|
+
return {
|
|
78
|
+
review_independence: independence,
|
|
79
|
+
acceptance_status: acceptanceStatus(independence),
|
|
80
|
+
assurance: assuranceFor(independence),
|
|
81
|
+
reviewers: [...memberNames],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Consistency violations for an artifact that already carries the fields.
|
|
87
|
+
*
|
|
88
|
+
* The gate's teeth: a producer that hand-set `accepted` beside a same-family
|
|
89
|
+
* independence is caught here rather than trusted.
|
|
90
|
+
*/
|
|
91
|
+
export function independenceViolations(doc: Record<string, unknown>): string[] {
|
|
92
|
+
const out: string[] = [];
|
|
93
|
+
const indep = doc['review_independence'];
|
|
94
|
+
const accept = doc['acceptance_status'];
|
|
95
|
+
const assur = doc['assurance'];
|
|
96
|
+
if (indep === undefined) {
|
|
97
|
+
out.push('review_independence is absent — an artifact that does not declare its independence reads as acceptance by default');
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
const expectedAccept = acceptanceStatus(indep as ReviewIndependence);
|
|
101
|
+
if (accept !== expectedAccept) {
|
|
102
|
+
out.push(
|
|
103
|
+
`acceptance_status is ${String(accept)} but review_independence ${String(indep)} derives ${expectedAccept}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
const expectedAssur = assuranceFor(indep as ReviewIndependence);
|
|
107
|
+
if (assur !== undefined && assur !== expectedAssur) {
|
|
108
|
+
out.push(`assurance is ${String(assur)} but review_independence ${String(indep)} derives ${expectedAssur}`);
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
@@ -212,6 +212,20 @@ export interface SessionRecord {
|
|
|
212
212
|
started_at: string;
|
|
213
213
|
/** ISO-8601 UTC, rewritten every heartbeat. The liveness signal. */
|
|
214
214
|
last_seen: string;
|
|
215
|
+
/**
|
|
216
|
+
* Per-detector turn-end refusals THIS session has taken, or absent when it
|
|
217
|
+
* has taken none.
|
|
218
|
+
*
|
|
219
|
+
* `road-to-stop-gate-honesty` § D-2: the gate refuses turn-ends with "no
|
|
220
|
+
* per-session visibility into how often it happens", and a refused turn-end
|
|
221
|
+
* costs the user at least one extra model turn. The counts ride this
|
|
222
|
+
* existing write — the reader is one small `readFileSync` of a record the
|
|
223
|
+
* gate already maintains, so the heartbeat gains no spawn.
|
|
224
|
+
*
|
|
225
|
+
* Optional on purpose: a record written before this field existed, or by a
|
|
226
|
+
* session that was never refused, is still a valid record.
|
|
227
|
+
*/
|
|
228
|
+
turn_end_refusals?: Record<string, number>;
|
|
215
229
|
}
|
|
216
230
|
|
|
217
231
|
/**
|
|
@@ -454,10 +454,27 @@ export function observationSourceOf(record: ObservationRecord): ObservationSourc
|
|
|
454
454
|
return record.observation_source ?? 'self-report';
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
+
/**
|
|
458
|
+
* The self-report path's record.
|
|
459
|
+
*
|
|
460
|
+
* `projectedSkillCount` and `projectionMode` are optional in the SAME shape the
|
|
461
|
+
* two host-event builders use — omitted rather than defaulted, because absence
|
|
462
|
+
* is not `legacy-all` and never was (see `ObservationRecord.projection_mode`).
|
|
463
|
+
*
|
|
464
|
+
* They were missing here until 2026-08-18, and the omission was load-bearing in
|
|
465
|
+
* one direction: the self-report path is the ONLY one that fills `bare_names`,
|
|
466
|
+
* so it is the only source the pointable-but-bare join below can read — and
|
|
467
|
+
* every claude observation in the corpus therefore carried no scope at all,
|
|
468
|
+
* while the codex records beside them did. A series that changes scope
|
|
469
|
+
* mid-flight without recording it is not comparable, which is exactly what
|
|
470
|
+
* claim 7 of the roadmap that asked for the field says.
|
|
471
|
+
*/
|
|
457
472
|
export function buildObservationRecord(
|
|
458
473
|
report: SelectorReport,
|
|
459
474
|
host: string,
|
|
460
475
|
observedAt: string,
|
|
476
|
+
projectedSkillCount?: number,
|
|
477
|
+
projectionMode?: ProjectionMode,
|
|
461
478
|
): ObservationRecord {
|
|
462
479
|
return {
|
|
463
480
|
schema: 1,
|
|
@@ -471,6 +488,8 @@ export function buildObservationRecord(
|
|
|
471
488
|
separating_candidates: report.candidates.filter((c) => c.separates).map((c) => c.id),
|
|
472
489
|
truncation_mode: 'per-entry',
|
|
473
490
|
observation_source: 'self-report',
|
|
491
|
+
...(projectedSkillCount === undefined ? {} : { projected_skill_count: projectedSkillCount }),
|
|
492
|
+
...(projectionMode === undefined ? {} : { projection_mode: projectionMode }),
|
|
474
493
|
};
|
|
475
494
|
}
|
|
476
495
|
|
|
@@ -1113,3 +1132,4 @@ export function formatReport(report: SelectorReport): string {
|
|
|
1113
1132
|
);
|
|
1114
1133
|
return lines.join('\n');
|
|
1115
1134
|
}
|
|
1135
|
+
|