@event4u/agent-config 17.0.0 → 17.2.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +157 -468
- package/README.md +4 -3
- package/dist/agent-src/commands/project/analyze.md +9 -6
- package/dist/agent-src/commands/project/map.md +58 -0
- package/dist/agent-src/commands/project.md +5 -3
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +5 -3
- package/dist/agent-src/skills/code-intelligence/SKILL.md +6 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +5 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/{chunk-IROAQ77A.js → chunk-3PK3VZHB.js} +107 -9
- package/dist/cli-delegate/{chunk-2SFOOFJJ.js → chunk-BZJ4Y3CI.js} +1 -1
- package/dist/cli-delegate/{chunk-CAL76ADI.js → chunk-CMZEKSNN.js} +52 -10
- package/dist/cli-delegate/{chunk-ESUNQ5XR.js → chunk-E3CFULNX.js} +22 -4
- package/dist/cli-delegate/{chunk-GOMILC3O.js → chunk-QBHEI5N4.js} +4 -3
- package/dist/cli-delegate/{chunk-SIOMDAKL.js → chunk-VYZGJLCA.js} +29 -39
- package/dist/cli-delegate/chunk-W5JXMF2T.js +77 -0
- package/dist/cli-delegate/{chunk-4SHNERBL.js → chunk-XIAPY44N.js} +1 -1
- package/dist/cli-delegate/{chunk-JHMMWNJ2.js → chunk-YHAPONWV.js} +1 -1
- package/dist/cli-delegate/cmd_conformance.js +32 -47
- package/dist/cli-delegate/cmd_doctor.js +4 -4
- package/dist/cli-delegate/cmd_doctor_neighbours.js +2 -2
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_export.js +6 -6
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_preflight.js +3 -3
- package/dist/cli-delegate/cmd_prune.js +6 -6
- package/dist/cli-delegate/cmd_settings_migrate.js +6 -6
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +6 -6
- package/dist/cli-delegate/cmd_uninstall.js +6 -6
- package/dist/cli-delegate/cmd_update.js +9 -13
- package/dist/cli-delegate/cmd_validate.js +6 -6
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +41 -15
- 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 +163 -148
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/instructions_observer.chunk.cjs +301 -0
- package/dist/hooks/instructions_observer.chunk.cjs.sha256 +1 -0
- package/dist/install/claudeRuleRewrite.js +10 -3
- package/dist/install/conflict.js +24 -2
- package/dist/install/conflictTracker.js +14 -3
- package/dist/install/install.mjs +222 -117
- package/dist/install/installThinLayer.js +1 -0
- package/dist/install/loggedWrites.js +142 -0
- package/dist/install/paths.js +12 -2
- package/dist/install/preserve.js +25 -4
- package/dist/install/txlog.js +10 -6
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +33595 -22916
- package/dist/scripts/_lib/egress_rules.js +4 -1
- package/dist/scripts/_lib/identifier_redaction.js +35 -2
- package/dist/scripts/ai_council/seat_policy.js +7 -1
- package/dist/server/schemas/settings.js +1 -1
- package/package.json +4 -1
- package/src/config/ci-local-parity.yml +1 -1
- package/src/config/continuity-surface.json +7 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +27 -7
- package/src/config/gate-violation-baselines.json +9 -3
- package/src/config/gitignore-block.txt +6 -0
- package/src/config/hook-bundle-budget.json +19 -4
- package/src/config/hook-token-budget.json +1 -1
- package/src/config/packed-binary-manifest.json +27 -0
- package/src/config/production-entries.json +18 -0
- package/src/config/release-gate-locality.yml +0 -7
- package/src/config/skill-body-budget.json +7 -1
- package/src/install/claudeRuleRewrite.ts +10 -1
- package/src/install/conflict.ts +24 -1
- package/src/install/conflictTracker.ts +18 -2
- package/src/install/installThinLayer.ts +6 -0
- package/src/install/loggedWrites.ts +148 -0
- package/src/install/paths.ts +11 -2
- package/src/install/preserve.ts +40 -4
- package/src/install/txlog.ts +10 -6
- package/src/scripts/_cli/cmd_conformance.ts +3 -2
- package/src/scripts/_cli/cmd_update.ts +15 -17
- package/src/scripts/_lib/bench_ab_complexity.ts +14 -2
- package/src/scripts/_lib/collector_record.ts +1 -0
- package/src/scripts/_lib/council_settings_block.ts +9 -2
- package/src/scripts/_lib/egress_rules.ts +4 -1
- package/src/scripts/_lib/host_capability.ts +10 -5
- package/src/scripts/_lib/identifier_redaction.ts +38 -2
- package/src/scripts/_lib/price_freshness.ts +112 -0
- package/src/scripts/_lib/production_reach.ts +194 -0
- package/src/scripts/_lib/quota_parked.ts +17 -28
- package/src/scripts/_lib/release_findings_ingest.ts +13 -335
- package/src/scripts/_lib/runtime_journal.ts +7 -7
- package/src/scripts/_lib/tier_downshift_verdict.ts +81 -35
- package/src/scripts/ai_council/chairman.ts +5 -2
- package/src/scripts/ai_council/clients.ts +4 -3
- package/src/scripts/ai_council/free_hand_shadow.ts +24 -12
- package/src/scripts/ai_council/free_route_catalog.ts +142 -5
- package/src/scripts/ai_council/free_route_client.ts +4 -0
- package/src/scripts/ai_council/hand_payload.ts +115 -7
- package/src/scripts/ai_council/orchestrator.ts +7 -10
- package/src/scripts/ai_council/route_chooser.ts +1 -1
- package/src/scripts/ai_council/route_member.ts +66 -4
- package/src/scripts/ai_council/seat_policy.ts +8 -1
- package/src/scripts/ai_council/self_hosted_probe.ts +327 -0
- package/src/scripts/ai_council/status_surface.ts +2 -0
- package/src/scripts/ask_transport.ts +137 -15
- package/src/scripts/check_finding_dispositions.ts +25 -71
- package/src/scripts/check_release_pr_shape.ts +3 -3
- package/src/scripts/check_step_production_reach.ts +257 -0
- package/src/scripts/code_graph/build.ts +210 -10
- package/src/scripts/code_graph/cli.ts +168 -46
- package/src/scripts/code_graph/detect.ts +5 -4
- package/src/scripts/code_graph/docs_layer.ts +57 -0
- package/src/scripts/code_graph/extract.ts +58 -13
- package/src/scripts/code_graph/extract_more.ts +314 -0
- package/src/scripts/code_graph/laravel_overlay.ts +454 -0
- package/src/scripts/code_graph/map.ts +846 -0
- package/src/scripts/code_graph/map_model.ts +977 -0
- package/src/scripts/code_graph/map_verify.ts +140 -0
- package/src/scripts/code_graph/map_viewer/viewer.css +278 -0
- package/src/scripts/code_graph/map_viewer/viewer.js +756 -0
- package/src/scripts/code_graph/scip_reader.ts +201 -0
- package/src/scripts/code_graph/sqlite_store.ts +8 -1
- package/src/scripts/code_graph/types.ts +81 -15
- package/src/scripts/code_graph/validate.ts +16 -26
- package/src/scripts/code_graph/verbs.ts +7 -0
- package/src/scripts/council_cli.ts +10 -8
- package/src/scripts/hook_chunks.mjs +7 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +23 -0
- package/src/scripts/hooks/dispatch_hook.ts +4 -4
- package/src/scripts/hooks/instructions_observer_hook.ts +25 -6
- package/src/scripts/hooks/lazy_concern.ts +4 -0
- package/src/scripts/hooks/payload_stub.ts +27 -0
- package/src/scripts/hooks/spawn_guard_shadow_hook.ts +29 -70
- package/src/scripts/hooks/subagent_ledger_hook.ts +80 -0
- package/src/scripts/install-hooks.sh +5 -0
- package/src/scripts/install.ts +2 -2
- package/src/scripts/lint_agents_layout.ts +2 -0
- package/src/scripts/lint_decision_classes.ts +34 -0
- package/src/scripts/lint_hook_manifest.ts +25 -0
- package/src/scripts/lint_roadmap_blockers.ts +52 -3
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -6
- package/src/scripts/mcp_server/graph_tools.ts +73 -1
- package/src/scripts/mcp_server/tools.ts +19 -36
- package/src/scripts/release.ts +5 -9
- package/src/scripts/release_drill.ts +11 -165
- package/src/scripts/release_publication.ts +0 -9
- package/src/scripts/report_production_reach.ts +35 -0
- package/src/scripts/worker_reuse_shadow_report.ts +399 -1
- package/src/server/schemas/settings.ts +1 -1
- package/src/vendor/grammars/README.md +10 -5
- package/src/vendor/grammars/tree-sitter-go.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-java.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-python.wasm +0 -0
- package/dist/cli-delegate/chunk-A4QJ6ENT.js +0 -50
- /package/dist/cli-delegate/{chunk-E6XNBVE4.js → chunk-KR5ZIFPO.js} +0 -0
|
@@ -1,46 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* What is left of the release's side of the findings ledger: where a ledger
|
|
3
|
+
* lives, and the `Known limitations` line a cut on an override owes.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* once per release, six times between 14.19.0 and 15.0.0, and each absence was
|
|
12
|
-
* cleared by whoever happened to be pushing an unrelated change.
|
|
13
|
-
*
|
|
14
|
-
* WHY THE INGEST IS NOT HERE. Two review rounds established that no placement
|
|
15
|
-
* inside `release.ts` works. `finding-dispositions` runs on the release PR and
|
|
16
|
-
* reds while the ledger lacks a finding the review reported, so a step that
|
|
17
|
-
* produced the ledger after the check wait could never run — the wait had
|
|
18
|
-
* already died on that red check — and a step that produced it before the wait
|
|
19
|
-
* pushed a commit the wait then had to redo, on a head whose checks had not
|
|
20
|
-
* started. The owner's decision on 2026-09-11 was to move the ingest into the
|
|
21
|
-
* workflow that already holds the artifact, which is the one place where the
|
|
22
|
-
* ledger can exist before any gate looks for it.
|
|
23
|
-
*
|
|
24
|
-
* WHY THE PUSH NEEDS A PAT. A third review round found that the move alone did
|
|
25
|
-
* not fix the ordering, it relocated it: the ingest job is a check run of the
|
|
26
|
-
* same `pull_request` event, so its push lands inside the release's own check
|
|
27
|
-
* wait — and a push authenticated with GITHUB_TOKEN creates no run, leaving a
|
|
28
|
-
* head with zero checks against a protection that requires one. The job pushes
|
|
29
|
-
* with `RELEASE_PR_TOKEN` instead, the pattern `release.yml` already uses, so
|
|
30
|
-
* the push produces the checks the wait is waiting for. Without that secret the
|
|
31
|
-
* job declines to push at all rather than produce the deadlock, and the absence
|
|
32
|
-
* arrives here as a missing ledger with the cause named.
|
|
33
|
-
*
|
|
34
|
-
* WHAT IS LEFT HERE. Two questions and a stop for each: is the ledger on the
|
|
35
|
-
* REMOTE branch, and does the disposition gate pass. Neither is repaired by
|
|
36
|
-
* the release — filling a disposition states what the release ships, with a
|
|
37
|
-
* rationale and a named verifier, and no automation writes one.
|
|
5
|
+
* A release no longer reads the ledger. Every commit on main is already
|
|
6
|
+
* reviewed and merged, so the cut waits for no review (owner, 2026-10-22);
|
|
7
|
+
* the 17.0.0 cut stopped for a day on a ledger whose self-review never
|
|
8
|
+
* finished. The self-review on a release PR is advisory. No release workflow
|
|
9
|
+
* waits for `check_finding_dispositions`; the consistency coverage guard still
|
|
10
|
+
* executes it, so any committed ledger must remain valid.
|
|
38
11
|
*/
|
|
39
12
|
import * as fs from 'node:fs';
|
|
40
13
|
import * as path from 'node:path';
|
|
41
14
|
|
|
42
|
-
import { isBlocking, parse_ledger } from '../check_finding_dispositions.js';
|
|
43
|
-
|
|
44
15
|
|
|
45
16
|
/** Where a release's ledger lives, relative to the repository root. */
|
|
46
17
|
export function ledgerRelPath(version: string): string {
|
|
@@ -50,11 +21,11 @@ export function ledgerRelPath(version: string): string {
|
|
|
50
21
|
/**
|
|
51
22
|
* The `Known limitations` line a cut taken on an override owes its readers.
|
|
52
23
|
*
|
|
53
|
-
* An override is
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
24
|
+
* An override is a ledger's explicit statement that its release was not
|
|
25
|
+
* self-reviewed, recorded where only a releaser looks — inside the ledger JSON.
|
|
26
|
+
* Since 2026-10-22 a release needs no ledger at all, so this line appears only
|
|
27
|
+
* for a ledger that carries an override; a release with no ledger publishes no
|
|
28
|
+
* limitation, because main is reviewed before the cut.
|
|
58
29
|
*
|
|
59
30
|
* Returns null for every ordinary release: no ledger, a reviewed one, or one
|
|
60
31
|
* carrying no override. The caller appends; it never replaces a derived line.
|
|
@@ -84,296 +55,3 @@ export function assuranceOverrideLimitation(version: string, repoRoot: string):
|
|
|
84
55
|
+ `was taken on an owner directive recorded at ${override}.`
|
|
85
56
|
);
|
|
86
57
|
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* The workflow that produces and commits the ledger, named so a message can
|
|
90
|
-
* point the operator at the right run log.
|
|
91
|
-
*
|
|
92
|
-
* A rename there without one here is caught by
|
|
93
|
-
* `release_findings_ingest.test.ts`, which reads the workflow file rather than
|
|
94
|
-
* trusting this string.
|
|
95
|
-
*/
|
|
96
|
-
export const FINDINGS_WORKFLOW = 'self-review-gate.yml';
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Argv proving the ledger is on the REMOTE branch — the only ref that answers
|
|
100
|
-
* the question the release is asking.
|
|
101
|
-
*
|
|
102
|
-
* Three refs give three different answers and only one is right. The working
|
|
103
|
-
* tree says "a file exists here", which a by-hand ingest also satisfies. The
|
|
104
|
-
* LOCAL branch ref says "a commit here carries it", which a commit that failed
|
|
105
|
-
* to push has already made true — so probing it answers identically to the
|
|
106
|
-
* filesystem in exactly the case the probe exists for, which is the defect a
|
|
107
|
-
* second review round caught in the first attempt at this fix. The
|
|
108
|
-
* remote-tracking ref is the one that goes false when a push fails, and the
|
|
109
|
-
* merge reads the remote.
|
|
110
|
-
*
|
|
111
|
-
* `remote` is a parameter rather than a constant so a fork or a mirror is not
|
|
112
|
-
* silently assumed to be `origin`.
|
|
113
|
-
*/
|
|
114
|
-
export function ledgerOnBranchArgv(remote: string, branch: string, rel: string): string[] {
|
|
115
|
-
return ['cat-file', '-e', `${remote}/${branch}:${rel}`];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Why the release refuses to continue without a ledger on the branch.
|
|
120
|
-
*
|
|
121
|
-
* Continuing would merge and tag with no ledger, and an absent ledger becomes a
|
|
122
|
-
* repo-wide failure the moment the tag exists — that is the six-release failure
|
|
123
|
-
* this mechanism ends, so it is a stop rather than a warning.
|
|
124
|
-
*
|
|
125
|
-
* The causes are named because the operator has to pick one and most of them
|
|
126
|
-
* are ordinary. With the ingest in CI, "it has not happened yet" is the common
|
|
127
|
-
* case and waiting is the answer; a repository without the review secret never
|
|
128
|
-
* gets one at all and needs the ledger by hand, once per release, which is a
|
|
129
|
-
* real cost and is stated here rather than discovered.
|
|
130
|
-
*/
|
|
131
|
-
export function ledgerAbsentMessage(version: string, branch: string, remote: string): string {
|
|
132
|
-
return (
|
|
133
|
-
`${ledgerRelPath(version)} is not on ${remote}/${branch}.\n` +
|
|
134
|
-
' The release stops: continuing would merge and tag with no findings ledger, and an ' +
|
|
135
|
-
'absent ledger for a shipped version reds every pull request in the repository.\n' +
|
|
136
|
-
` The ${FINDINGS_WORKFLOW} workflow commits it to the release branch once it has ` +
|
|
137
|
-
'reviewed the head. Ordinary causes, in the order to check them: the review has not ' +
|
|
138
|
-
'finished yet (wait, then resume); the review found nothing and its commit is still in ' +
|
|
139
|
-
'flight (same); no ANTHROPIC_API_KEY, so no review ran in CI and no ledger will appear — ' +
|
|
140
|
-
'review it locally instead, which needs no credit because the gate resolves the ' +
|
|
141
|
-
'subscription CLI before any metered key: `./scripts-run src/scripts/self_review_gate ' +
|
|
142
|
-
'--findings-out agents/runtime/release-findings.json`, then `./scripts-run ' +
|
|
143
|
-
'src/scripts/check_finding_dispositions --ingest agents/runtime/release-findings.json --release ' +
|
|
144
|
-
`${version}\`, and push the ledger to ${branch}; no RELEASE_PR_TOKEN, in which ` +
|
|
145
|
-
'case the job refuses to push rather than deadlock this wait and says so as a warning in ' +
|
|
146
|
-
'its run log — configure the PAT, or write the ledger by hand; or the workflow could not ' +
|
|
147
|
-
'push, which its run log will say.\n' +
|
|
148
|
-
' Then: `git pull` on the release branch, and resume.'
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Why the release refuses to continue when the PR is already merged and the
|
|
154
|
-
* ledger is not on the trunk either.
|
|
155
|
-
*
|
|
156
|
-
* This is the state the whole mechanism exists to prevent, reached the one way
|
|
157
|
-
* that survives every guard before it: the ingest produced nothing, step 7
|
|
158
|
-
* stopped, and the PR was merged by hand anyway. A resumed run then finds
|
|
159
|
-
* `state === 'MERGED'` and — until this check existed — printed "the ledger
|
|
160
|
-
* rode in with it" without reading anything, tagged, and published. The absent
|
|
161
|
-
* ledger for a shipped version is what reds every pull request in the
|
|
162
|
-
* repository, so the assertion had to become a question.
|
|
163
|
-
*
|
|
164
|
-
* The release branch is gone by now, so there is no workflow run left to wait
|
|
165
|
-
* for: the remaining path is a ledger written onto the trunk by hand.
|
|
166
|
-
*/
|
|
167
|
-
export function ledgerAbsentAfterMergeMessage(
|
|
168
|
-
version: string,
|
|
169
|
-
trunk: string,
|
|
170
|
-
remote: string,
|
|
171
|
-
): string {
|
|
172
|
-
return (
|
|
173
|
-
`${ledgerRelPath(version)} is on neither the release branch nor ${remote}/${trunk}, and ` +
|
|
174
|
-
'the release pull request is already merged.\n' +
|
|
175
|
-
' The release stops: tagging now ships a version whose findings ledger does not exist, ' +
|
|
176
|
-
'and an absent ledger for a shipped version reds every pull request in the repository.\n' +
|
|
177
|
-
` The release branch is gone, so the ${FINDINGS_WORKFLOW} run that would have committed ` +
|
|
178
|
-
'the ledger cannot be waited for. Put the ledger on the trunk instead: recover what the ' +
|
|
179
|
-
"review for this version reported in its own workflow run, or — if none ran — review it " +
|
|
180
|
-
'now with `./scripts-run src/scripts/self_review_gate --findings-out agents/runtime/release-findings.json`, ' +
|
|
181
|
-
'which needs no credit because the gate resolves the subscription CLI before any metered ' +
|
|
182
|
-
`key. Ingest with \`--ingest agents/runtime/release-findings.json --release ${version}\`, push, and resume.\n` +
|
|
183
|
-
' A ledger that reviewed nothing no longer clears the cut on a reason string: an ' +
|
|
184
|
-
'`assurance: unreviewed` ledger is refused unless it carries an `assurance_override` ' +
|
|
185
|
-
'naming a committed owner directive.'
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* The message a release stops with when the disposition gate refuses.
|
|
191
|
-
*
|
|
192
|
-
* `blocking` is what the release could count itself: findings that are blocking
|
|
193
|
-
* and carry no `status`. It is deliberately NOT presented as the reason — the
|
|
194
|
-
* gate refuses for more shapes than that (an unknown status, an empty rationale
|
|
195
|
-
* or verifier, a `fixed` with no commit, a malformed ledger, an undeterminable
|
|
196
|
-
* release status), and an earlier version of this message asserted "N blocking
|
|
197
|
-
* … carry no disposition" for every one of them. At N = 0 that produced an
|
|
198
|
-
* instruction nobody could follow.
|
|
199
|
-
*
|
|
200
|
-
* So the gate's own output is the reason, passed in and printed, and this text
|
|
201
|
-
* only says where the file is, why the release stopped, and how to resume.
|
|
202
|
-
*/
|
|
203
|
-
export function dispositionStopMessage(
|
|
204
|
-
version: string,
|
|
205
|
-
gateOutput: string,
|
|
206
|
-
blocking: number,
|
|
207
|
-
resumeCmd: string,
|
|
208
|
-
): string {
|
|
209
|
-
const count =
|
|
210
|
-
blocking > 0
|
|
211
|
-
? ` ${blocking} of them are blocking findings with no disposition yet (no status, or still_open).\n`
|
|
212
|
-
: '';
|
|
213
|
-
return (
|
|
214
|
-
`the disposition gate refuses ${version}. Its own report:\n` +
|
|
215
|
-
`${gateOutput.trim() || ' (the gate printed nothing — run it directly to see why)'}\n` +
|
|
216
|
-
count +
|
|
217
|
-
` The ledger is at ${ledgerRelPath(version)} and the release stops here by design: a ` +
|
|
218
|
-
'disposition is an adjudication of what this release ships, with a rationale and a ' +
|
|
219
|
-
'named verifier, and no automation may write one.\n' +
|
|
220
|
-
` Resolve what the report names, push the release branch, then: ${resumeCmd}`
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/** The release's own subprocess result shape, mirrored so this module stays free of it. */
|
|
225
|
-
interface StepRunResult {
|
|
226
|
-
returncode: number;
|
|
227
|
-
stdout: string;
|
|
228
|
-
stderr: string;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* What the step needs from the release script, injected rather than imported.
|
|
233
|
-
*
|
|
234
|
-
* `release.ts` sits past the source-size cap where every line is charged, and
|
|
235
|
-
* this step is the release's side of the contract this module already owns — so
|
|
236
|
-
* it lives here. Injection rather than an import of `release_publication.ts`
|
|
237
|
-
* keeps `_lib` free of a dependency on its own caller.
|
|
238
|
-
*/
|
|
239
|
-
export interface LedgerStepDeps {
|
|
240
|
-
run: (argv: string[], opts: { check: boolean; capture: boolean }) => StepRunResult;
|
|
241
|
-
die: (msg: string) => never;
|
|
242
|
-
/** Prints the step banner, so the caller keeps the step number. */
|
|
243
|
-
step: (msg: string) => void;
|
|
244
|
-
/** Where the step reports the ref it read. */
|
|
245
|
-
write: (s: string) => void;
|
|
246
|
-
/** Absolute repository root, for resolving the ledger on disk. */
|
|
247
|
-
repoRoot: string;
|
|
248
|
-
/** Remote name, never assumed to be `origin`. */
|
|
249
|
-
remote: string;
|
|
250
|
-
/** The trunk the release merges into — the ref to ask once the branch is gone. */
|
|
251
|
-
trunk: string;
|
|
252
|
-
/**
|
|
253
|
-
* The PR number for `--pr`, resolved late.
|
|
254
|
-
*
|
|
255
|
-
* Late because the release probes its PR once at the top and only on a
|
|
256
|
-
* resumed run, so on a first run the number was null here and `--pr` was
|
|
257
|
-
* silently dropped — and the release then passed a strictly weaker check
|
|
258
|
-
* than `finding-dispositions`, learning about an un-ingested finding from
|
|
259
|
-
* CI instead. By the time this step runs the PR exists either way.
|
|
260
|
-
*/
|
|
261
|
-
resolvePr: () => number | null;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Verify the release carries its own findings ledger, or stop.
|
|
266
|
-
*
|
|
267
|
-
* VERIFIES, NEVER PRODUCES — the why is the module docstring above. Placed
|
|
268
|
-
* between the check wait and the merge: the ledger lives on the release branch
|
|
269
|
-
* and the merge deletes it, so this is the last moment the question means
|
|
270
|
-
* anything, and after the wait so a red `finding-dispositions` has already
|
|
271
|
-
* stopped the release with the check's own name rather than a duplicate of it.
|
|
272
|
-
*
|
|
273
|
-
* Two questions, each answered with a stop:
|
|
274
|
-
*
|
|
275
|
-
* 1. Is the ledger on the ref the merge will read? On an open PR that is the
|
|
276
|
-
* remote release branch; once the PR is merged the branch is deleted and
|
|
277
|
-
* the ledger, if it was ever produced, rode into the trunk. The merged
|
|
278
|
-
* path used to ask nothing and assert the good case.
|
|
279
|
-
* 2. Does the disposition gate pass, in `--pr` mode — the same mode CI uses,
|
|
280
|
-
* so the release cannot pass a weaker check than the one it is trying to
|
|
281
|
-
* pre-satisfy.
|
|
282
|
-
*
|
|
283
|
-
* Neither answer is repaired here. Filling a disposition states what the release
|
|
284
|
-
* ships, with a rationale and a named verifier; no automation writes one.
|
|
285
|
-
*/
|
|
286
|
-
export function settleFindingsLedger(
|
|
287
|
-
deps: LedgerStepDeps,
|
|
288
|
-
version: string,
|
|
289
|
-
branch: string,
|
|
290
|
-
merged: boolean,
|
|
291
|
-
): void {
|
|
292
|
-
deps.step(
|
|
293
|
-
merged
|
|
294
|
-
? 'Verify the findings ledger rode in with the merge'
|
|
295
|
-
: 'Verify the self-review findings ledger',
|
|
296
|
-
);
|
|
297
|
-
const rel = ledgerRelPath(version);
|
|
298
|
-
const ref = merged ? deps.trunk : branch;
|
|
299
|
-
|
|
300
|
-
// REFRESH BEFORE READING, AND FAST-FORWARD BEFORE ASKING THE GATE. Both
|
|
301
|
-
// halves are a defect a fourth review measured, and they are the same
|
|
302
|
-
// defect seen from two sides. `cat-file -e <remote>/<ref>:<path>` reads
|
|
303
|
-
// `.git`, not GitHub — and nothing in a first run refreshes that
|
|
304
|
-
// remote-tracking ref after CI commits the ledger: preflight fetches before
|
|
305
|
-
// the branch exists, and step 4's push sets the ref to the pushed commit,
|
|
306
|
-
// which is strictly older than the ledger commit. So the probe answered
|
|
307
|
-
// "absent" on every first release. The disposition gate then reads the
|
|
308
|
-
// WORKING TREE, which the same push left equally stale, so a fetch alone
|
|
309
|
-
// would fix the probe and hand the gate an empty ledger instead.
|
|
310
|
-
//
|
|
311
|
-
// `--ff-only` on purpose: the release pushed this ref itself, so the only
|
|
312
|
-
// commit ahead of it should be CI's. Anything else means the branch moved
|
|
313
|
-
// under the run, and stopping is better than merging a tree nobody read.
|
|
314
|
-
// Both are `check: false` — a fetch that fails leaves the probe to report
|
|
315
|
-
// the absence with a message that says what to do.
|
|
316
|
-
deps.run(['git', 'fetch', deps.remote, ref], { check: false, capture: true });
|
|
317
|
-
deps.run(['git', 'pull', '--ff-only', deps.remote, ref], { check: false, capture: true });
|
|
318
|
-
|
|
319
|
-
const onBranch =
|
|
320
|
-
deps.run(['git', ...ledgerOnBranchArgv(deps.remote, ref, rel)], {
|
|
321
|
-
check: false,
|
|
322
|
-
capture: true,
|
|
323
|
-
}).returncode === 0;
|
|
324
|
-
if (!onBranch) {
|
|
325
|
-
deps.die(
|
|
326
|
-
merged
|
|
327
|
-
? ledgerAbsentAfterMergeMessage(version, deps.trunk, deps.remote)
|
|
328
|
-
: ledgerAbsentMessage(version, branch, deps.remote),
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
deps.write(` ledger on ${deps.remote}/${ref}: ${rel}\n`);
|
|
332
|
-
|
|
333
|
-
const argv = ['./scripts-run', 'src/scripts/check_finding_dispositions', '--release', version];
|
|
334
|
-
const pr = deps.resolvePr();
|
|
335
|
-
if (pr !== null) {
|
|
336
|
-
argv.push('--pr', String(pr));
|
|
337
|
-
}
|
|
338
|
-
const verdict = deps.run(argv, { check: false, capture: true });
|
|
339
|
-
if (verdict.returncode !== 0) {
|
|
340
|
-
deps.die(
|
|
341
|
-
dispositionStopMessage(
|
|
342
|
-
version,
|
|
343
|
-
// BOTH streams. The gate writes its scan line to stdout and its
|
|
344
|
-
// per-finding diagnosis to stderr, so preferring stdout showed
|
|
345
|
-
// the operator `scanned: N` and discarded the entire reason the
|
|
346
|
-
// release stopped.
|
|
347
|
-
[verdict.stdout, verdict.stderr].map((s) => s.trim()).filter(Boolean).join('\n'),
|
|
348
|
-
blockingWithoutDisposition(path.join(deps.repoRoot, rel), version),
|
|
349
|
-
'task release -- --resume --yes',
|
|
350
|
-
),
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* How many blocking findings carry no disposition — no status, or `still_open`,
|
|
357
|
-
* which a blocking row may not rest on — a figure for the stop message, never
|
|
358
|
-
* the reason for it.
|
|
359
|
-
*
|
|
360
|
-
* The gate refuses for more shapes than this counts (an unknown status, an
|
|
361
|
-
* empty rationale or verifier, a `fixed` with no commit, a malformed ledger),
|
|
362
|
-
* which is why the gate's own output is what the operator is shown. It
|
|
363
|
-
* classifies against the version being released, the same input the gate
|
|
364
|
-
* reads from `--release`, not the ledger file's own `release` field.
|
|
365
|
-
*/
|
|
366
|
-
export function blockingWithoutDisposition(ledgerAbs: string, version: string): number {
|
|
367
|
-
if (!fs.existsSync(ledgerAbs)) {
|
|
368
|
-
return 0;
|
|
369
|
-
}
|
|
370
|
-
try {
|
|
371
|
-
const ledger = parse_ledger(fs.readFileSync(ledgerAbs, 'utf-8'), ledgerAbs);
|
|
372
|
-
return ledger.findings.filter((f) => {
|
|
373
|
-
const status = (f.status ?? '').trim();
|
|
374
|
-
return isBlocking(f, version) && (status === '' || status === 'still_open');
|
|
375
|
-
}).length;
|
|
376
|
-
} catch {
|
|
377
|
-
return 0;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
@@ -248,18 +248,18 @@ export const RECORDED_EVENTS: ReadonlySet<string> = new Set([
|
|
|
248
248
|
'stop',
|
|
249
249
|
'pre_compact',
|
|
250
250
|
'agent_error',
|
|
251
|
-
'subagent_start',
|
|
252
|
-
'
|
|
251
|
+
'subagent_start', 'subagent_stop',
|
|
252
|
+
'instructions_loaded',
|
|
253
253
|
]);
|
|
254
254
|
|
|
255
255
|
/**
|
|
256
|
-
* Members deliberately NOT recorded, each with the reason — silence
|
|
257
|
-
*
|
|
256
|
+
* Members deliberately NOT recorded, each with the reason — silence is not coverage.
|
|
257
|
+
* "Recorded" means accepted, not bound: see the manifest's `instructions_loaded` row.
|
|
258
258
|
*
|
|
259
|
-
* **Currently empty, and the emptiness is the finding.** All
|
|
260
|
-
* vocabulary are recordable: the journal stores what the dispatcher hands it
|
|
259
|
+
* **Currently empty, and the emptiness is the finding.** All eleven members of
|
|
260
|
+
* the vocabulary are recordable: the journal stores what the dispatcher hands it
|
|
261
261
|
* and has no per-event exclusion. The value of the partition is therefore not
|
|
262
|
-
* what it excludes today but that
|
|
262
|
+
* what it excludes today but that a twelfth member cannot arrive without a
|
|
263
263
|
* decision — the coverage test fails until it is placed in one set or the
|
|
264
264
|
* other, with a reason if it lands here.
|
|
265
265
|
*/
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
* inherited tier, both verified by the same deterministic check.
|
|
6
6
|
*
|
|
7
7
|
* Pure: the bars are constants here, the rows come from the caller, and an
|
|
8
|
-
* underpowered run backs nothing in either direction.
|
|
8
|
+
* underpowered run backs nothing in either direction. A price row that
|
|
9
|
+
* `price_freshness` refuses prices nothing, which leaves the cost verdict
|
|
10
|
+
* underpowered rather than read at a stale rate.
|
|
9
11
|
*/
|
|
12
|
+
import { type PriceRow, selectPrice } from './price_freshness.js';
|
|
10
13
|
|
|
11
14
|
/** Quality bar: the lite arm's fail-or-escalate rate may exceed inherit's by at most this. */
|
|
12
15
|
export const QUALITY_MARGIN_PP = 5;
|
|
@@ -16,6 +19,12 @@ export const ESCALATION_KILL = 0.3;
|
|
|
16
19
|
export const PAIR_CAP = 60;
|
|
17
20
|
/** Below this many complete pairs no rate is read at all. */
|
|
18
21
|
export const MIN_PAIRS = 30;
|
|
22
|
+
/**
|
|
23
|
+
* One-sided error of every bound read here. 0.05 is the level the
|
|
24
|
+
* pre-registered verdict already used: the rule of three it replaces is the
|
|
25
|
+
* one-sided 95 % bound for zero events.
|
|
26
|
+
*/
|
|
27
|
+
export const BOUND_ALPHA = 0.05;
|
|
19
28
|
|
|
20
29
|
export type Arm = 'lite' | 'inherit';
|
|
21
30
|
|
|
@@ -27,14 +36,6 @@ export interface LegUsage {
|
|
|
27
36
|
output: number;
|
|
28
37
|
}
|
|
29
38
|
|
|
30
|
-
/** USD per million tokens for one model family. */
|
|
31
|
-
export interface Price {
|
|
32
|
-
input: number;
|
|
33
|
-
output: number;
|
|
34
|
-
cache_write: number;
|
|
35
|
-
cache_read: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
39
|
export interface LegResult {
|
|
39
40
|
slice_id: string;
|
|
40
41
|
arm: Arm;
|
|
@@ -68,8 +69,15 @@ export interface TierDownshiftVerdict {
|
|
|
68
69
|
inherit: ArmSummary;
|
|
69
70
|
/** lite minus inherit fail-or-escalate rate, in percentage points. */
|
|
70
71
|
diff_pp: number | null;
|
|
71
|
-
/**
|
|
72
|
-
|
|
72
|
+
/**
|
|
73
|
+
* One-sided upper bound of the paired difference, in percentage points: the
|
|
74
|
+
* exact (Clopper–Pearson) bound on the rate of pairs only lite failed. The
|
|
75
|
+
* pairs only inherit failed can only lower the true difference, so leaving
|
|
76
|
+
* them out keeps the bound conservative and monotone in lite's failures.
|
|
77
|
+
*/
|
|
78
|
+
upper_bound_pp: number | null;
|
|
79
|
+
/** One-sided lower bound of the paired difference, in percentage points. */
|
|
80
|
+
lower_bound_pp: number | null;
|
|
73
81
|
escalation_rate_lite: number | null;
|
|
74
82
|
quality: QualityVerdict;
|
|
75
83
|
cost: CostVerdict;
|
|
@@ -78,27 +86,66 @@ export interface TierDownshiftVerdict {
|
|
|
78
86
|
notes: string[];
|
|
79
87
|
}
|
|
80
88
|
|
|
81
|
-
|
|
89
|
+
/** P(X <= k) for X ~ Binomial(n, p), summed in log space so large n stays finite. */
|
|
90
|
+
function binomCdf(k: number, n: number, p: number): number {
|
|
91
|
+
if (k < 0) return 0;
|
|
92
|
+
if (k >= n) return 1;
|
|
93
|
+
if (p <= 0) return 1;
|
|
94
|
+
if (p >= 1) return 0;
|
|
95
|
+
let logC = 0;
|
|
96
|
+
let sum = 0;
|
|
97
|
+
for (let i = 0; i <= k; i++) {
|
|
98
|
+
if (i > 0) logC += Math.log(n - i + 1) - Math.log(i);
|
|
99
|
+
sum += Math.exp(logC + i * Math.log(p) + (n - i) * Math.log1p(-p));
|
|
100
|
+
}
|
|
101
|
+
return Math.min(sum, 1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Bisection on a monotone function of p; 200 halvings is below double precision. */
|
|
105
|
+
function solve(f: (p: number) => number, target: number, decreasing: boolean): number {
|
|
106
|
+
let lo = 0;
|
|
107
|
+
let hi = 1;
|
|
108
|
+
for (let i = 0; i < 200; i++) {
|
|
109
|
+
const mid = (lo + hi) / 2;
|
|
110
|
+
const above = f(mid) > target;
|
|
111
|
+
if (above === decreasing) lo = mid;
|
|
112
|
+
else hi = mid;
|
|
113
|
+
}
|
|
114
|
+
return (lo + hi) / 2;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Exact one-sided upper bound on a binomial rate after `k` events in `n` trials. */
|
|
118
|
+
export function clopperPearsonUpper(k: number, n: number, alpha = BOUND_ALPHA): number {
|
|
119
|
+
if (k >= n) return 1;
|
|
120
|
+
return solve((p) => binomCdf(k, n, p), alpha, true);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Exact one-sided lower bound on a binomial rate after `k` events in `n` trials. */
|
|
124
|
+
export function clopperPearsonLower(k: number, n: number, alpha = BOUND_ALPHA): number {
|
|
125
|
+
if (k <= 0) return 0;
|
|
126
|
+
return solve((p) => 1 - binomCdf(k - 1, n, p), alpha, false);
|
|
127
|
+
}
|
|
82
128
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return family ? (prices[family] ?? null) : null;
|
|
129
|
+
interface PriceContext {
|
|
130
|
+
rows: readonly PriceRow[];
|
|
131
|
+
asOf: Date;
|
|
87
132
|
}
|
|
88
133
|
|
|
89
|
-
function legCost(model: string, usage: LegUsage, prices:
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
|
|
134
|
+
function legCost(model: string, usage: LegUsage, prices: PriceContext, notes: string[]): number {
|
|
135
|
+
const pick = selectPrice(prices.rows, model, prices.asOf);
|
|
136
|
+
if ('refusal' in pick) {
|
|
137
|
+
const note = `${pick.refusal}; its leg is not priced and the cost verdict is underpowered`;
|
|
138
|
+
if (!notes.includes(note)) notes.push(note);
|
|
93
139
|
return Number.NaN;
|
|
94
140
|
}
|
|
141
|
+
const p = pick.price;
|
|
95
142
|
return (
|
|
96
143
|
(usage.input * p.input + usage.cache_read * p.cache_read + usage.cache_write * p.cache_write + usage.output * p.output) /
|
|
97
144
|
1_000_000
|
|
98
145
|
);
|
|
99
146
|
}
|
|
100
147
|
|
|
101
|
-
function summarise(legs: LegResult[], prices:
|
|
148
|
+
function summarise(legs: LegResult[], prices: PriceContext, notes: string[]): ArmSummary {
|
|
102
149
|
let cost = 0;
|
|
103
150
|
let verified = 0;
|
|
104
151
|
let failedOrEscalated = 0;
|
|
@@ -129,8 +176,9 @@ function summarise(legs: LegResult[], prices: Record<string, Price>, notes: stri
|
|
|
129
176
|
* Read the paired run. A slice counts only when both arms ran it once; extra
|
|
130
177
|
* or one-sided rows are noted and left out rather than guessed at.
|
|
131
178
|
*/
|
|
132
|
-
export function tierDownshiftVerdict(rows: readonly LegResult[],
|
|
179
|
+
export function tierDownshiftVerdict(rows: readonly LegResult[], priceRows: readonly PriceRow[], asOf: Date): TierDownshiftVerdict {
|
|
133
180
|
const notes: string[] = [];
|
|
181
|
+
const prices: PriceContext = { rows: priceRows, asOf };
|
|
134
182
|
const bySlice = new Map<string, Partial<Record<Arm, LegResult>>>();
|
|
135
183
|
for (const r of rows) {
|
|
136
184
|
const entry = bySlice.get(r.slice_id) ?? {};
|
|
@@ -153,7 +201,8 @@ export function tierDownshiftVerdict(rows: readonly LegResult[], prices: Record<
|
|
|
153
201
|
const n = pairs.length;
|
|
154
202
|
|
|
155
203
|
let diffPp: number | null = null;
|
|
156
|
-
let
|
|
204
|
+
let upperPp: number | null = null;
|
|
205
|
+
let lowerPp: number | null = null;
|
|
157
206
|
let escalationRate: number | null = null;
|
|
158
207
|
if (n > 0) {
|
|
159
208
|
// Paired difference of the fail-or-escalate indicator: only discordant pairs move it.
|
|
@@ -165,27 +214,23 @@ export function tierDownshiftVerdict(rows: readonly LegResult[], prices: Record<
|
|
|
165
214
|
if (lf && !inf) b++;
|
|
166
215
|
if (!lf && inf) c++;
|
|
167
216
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
halfWidthPp = 1.96 * Math.sqrt(Math.max(variance, 0)) * 100;
|
|
172
|
-
// No discordant pair gives a zero-width interval; the rule of three bounds
|
|
173
|
-
// an unseen disagreement rate at 3/n instead.
|
|
174
|
-
if (b + c === 0) halfWidthPp = (3 / n) * 100;
|
|
217
|
+
diffPp = ((b - c) / n) * 100;
|
|
218
|
+
upperPp = clopperPearsonUpper(b, n) * 100;
|
|
219
|
+
lowerPp = (clopperPearsonLower(b, n) - clopperPearsonUpper(c, n)) * 100;
|
|
175
220
|
escalationRate = lite.escalated / n;
|
|
176
221
|
}
|
|
177
222
|
|
|
178
223
|
let quality: QualityVerdict;
|
|
179
|
-
if (n < MIN_PAIRS || diffPp === null ||
|
|
224
|
+
if (n < MIN_PAIRS || diffPp === null || upperPp === null || lowerPp === null || escalationRate === null) {
|
|
180
225
|
quality = 'underpowered';
|
|
181
226
|
notes.push(`${n} complete pairs, below the floor of ${MIN_PAIRS}`);
|
|
182
|
-
} else if (escalationRate >= ESCALATION_KILL ||
|
|
227
|
+
} else if (escalationRate >= ESCALATION_KILL || lowerPp > QUALITY_MARGIN_PP) {
|
|
183
228
|
quality = 'breached';
|
|
184
|
-
} else if (
|
|
229
|
+
} else if (upperPp < QUALITY_MARGIN_PP) {
|
|
185
230
|
quality = 'held';
|
|
186
231
|
} else {
|
|
187
232
|
quality = 'underpowered';
|
|
188
|
-
notes.push(
|
|
233
|
+
notes.push(`the upper bound ${upperPp.toFixed(2)} pp is not below the ${QUALITY_MARGIN_PP} pp margin`);
|
|
189
234
|
}
|
|
190
235
|
|
|
191
236
|
let cost: CostVerdict;
|
|
@@ -200,7 +245,8 @@ export function tierDownshiftVerdict(rows: readonly LegResult[], prices: Record<
|
|
|
200
245
|
lite,
|
|
201
246
|
inherit,
|
|
202
247
|
diff_pp: diffPp,
|
|
203
|
-
|
|
248
|
+
upper_bound_pp: upperPp,
|
|
249
|
+
lower_bound_pp: lowerPp,
|
|
204
250
|
escalation_rate_lite: escalationRate,
|
|
205
251
|
quality,
|
|
206
252
|
cost,
|
|
@@ -26,6 +26,8 @@ export interface ChairmanCandidate {
|
|
|
26
26
|
tier: number | null;
|
|
27
27
|
/** The seat's billing class; an unpaid one never chairs (ADR-257). Absent = paid. */
|
|
28
28
|
billing?: string | null;
|
|
29
|
+
/** A `base_url` route member; it never chairs, whatever its billing. */
|
|
30
|
+
route?: boolean;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export interface ChairmanSelection {
|
|
@@ -60,7 +62,8 @@ export function select_chairman(
|
|
|
60
62
|
annotation: `Chairman: host (member ${_q(member)} unavailable — host fallback)`,
|
|
61
63
|
};
|
|
62
64
|
}
|
|
63
|
-
const
|
|
65
|
+
const c = candidates.find((x) => x.name === member);
|
|
66
|
+
const refusal = seatRoleRefusal('synthesis', c?.billing, member, c?.route === true);
|
|
64
67
|
if (refusal !== null) {
|
|
65
68
|
return {
|
|
66
69
|
member: null,
|
|
@@ -85,7 +88,7 @@ export function select_chairman(
|
|
|
85
88
|
annotation: 'Chairman: host (no non-panel member available)',
|
|
86
89
|
};
|
|
87
90
|
}
|
|
88
|
-
const pool = nonPanel.filter((c) => seatRoleRefusal('synthesis', c.billing) === null);
|
|
91
|
+
const pool = nonPanel.filter((c) => seatRoleRefusal('synthesis', c.billing, c.name, c.route === true) === null);
|
|
89
92
|
if (pool.length === 0) {
|
|
90
93
|
return { member: null, annotation: 'Chairman: host (no paid non-panel member available)' };
|
|
91
94
|
}
|
|
@@ -1189,9 +1189,10 @@ export abstract class CliClient extends ExternalAIClient {
|
|
|
1189
1189
|
protected _cli_calls_path: string | null;
|
|
1190
1190
|
/** Attribution label for every booking this client makes. */
|
|
1191
1191
|
protected _consumer: string;
|
|
1192
|
-
/** The session the last answered call ran in; `resumeNext` continues it once,
|
|
1192
|
+
/** The session the last answered call ran in; `resumeNext` continues it once, never while `blind`. */
|
|
1193
1193
|
lastSession: SeatSession | null = null;
|
|
1194
1194
|
resumeNext: SeatSession | null = null;
|
|
1195
|
+
blind = false;
|
|
1195
1196
|
|
|
1196
1197
|
static _AUTH_FAILURE_PATTERNS: readonly string[] = [
|
|
1197
1198
|
'authentication',
|
|
@@ -1675,7 +1676,7 @@ export class AnthropicCliClient extends CliClient {
|
|
|
1675
1676
|
void max_tokens;
|
|
1676
1677
|
return [
|
|
1677
1678
|
this.binary,
|
|
1678
|
-
...seatResumeArgs(this.name, this.resumeNext, { blind:
|
|
1679
|
+
...seatResumeArgs(this.name, this.resumeNext, { blind: this.blind, now_ms: Date.now() }),
|
|
1679
1680
|
'--print',
|
|
1680
1681
|
// The seat never needs the user environment; only a version the light
|
|
1681
1682
|
// launch was probed on gets the flag (`LIGHT_LAUNCH_PROBES`).
|
|
@@ -1822,7 +1823,7 @@ export class OpenAICliClient extends CliClient {
|
|
|
1822
1823
|
// the payload itself is assembled in `_stdin_payload`.
|
|
1823
1824
|
const modelArgs =
|
|
1824
1825
|
this.model === OPENAI_CLI_VENDOR_DEFAULT ? [] : ['--model', this.model];
|
|
1825
|
-
const resume = seatResumeArgs(this.name, this.resumeNext, { blind:
|
|
1826
|
+
const resume = seatResumeArgs(this.name, this.resumeNext, { blind: this.blind, now_ms: Date.now() });
|
|
1826
1827
|
const bound = resume.length === 0 ? boundFor('openai') : resumeBound(boundFor('openai'));
|
|
1827
1828
|
return [this.binary, 'exec', ...resume, '--json', '--skip-git-repo-check', ...bound, ...modelArgs, '-'];
|
|
1828
1829
|
}
|