@event4u/agent-config 14.14.0 → 14.15.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 +63 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/pr/create.md +16 -1
- package/dist/agent-src/contexts/execution/terminal-states.md +25 -4
- package/dist/agent-src/rules/missing-skill-recovery.md +2 -2
- package/dist/agent-src/rules/token-budget-discipline.md +2 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli-delegate/{chunk-ILMUPLGW.js → chunk-7RXIKF62.js} +159 -86
- package/dist/cli-delegate/cmd_conformance.js +513 -17
- package/dist/cli-delegate/cmd_doctor.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +5 -5
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1043 -390
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +703 -115
- package/package.json +1 -1
- package/src/config/assurance-capability-registry.json +1 -1
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +62 -2
- package/src/config/gate-reachability-exemptions.json +1 -0
- package/src/config/gate-violation-baselines.json +4 -3
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/config/web-launch-readiness.json +4 -2
- package/src/scripts/_cli/_doctor_emit.ts +71 -0
- package/src/scripts/_cli/_doctor_strict.ts +163 -0
- package/src/scripts/_cli/cmd_doctor.ts +16 -37
- package/src/scripts/_dispatch.bash +11 -0
- package/src/scripts/_lib/branch_convergence.ts +186 -0
- package/src/scripts/_lib/context_observation.ts +132 -0
- package/src/scripts/_lib/continuation_ladder.ts +188 -0
- package/src/scripts/_lib/experiment_binding.ts +95 -0
- package/src/scripts/_lib/ignored_blocker.ts +8 -1
- package/src/scripts/_lib/outcome_envelope.ts +6 -1
- package/src/scripts/_lib/outcome_vocabularies.ts +135 -5
- package/src/scripts/_lib/release_highlights.ts +75 -10
- package/src/scripts/_lib/repeated_failure.ts +19 -2
- package/src/scripts/_lib/runtime_journal.ts +11 -4
- package/src/scripts/check_agent_artifact_location.ts +337 -0
- package/src/scripts/check_artefact_count_messaging.ts +74 -8
- package/src/scripts/check_enforcement_coverage.ts +40 -1
- package/src/scripts/check_release_highlights.ts +17 -1
- package/src/scripts/check_web_launch_readiness.ts +310 -20
- package/src/scripts/evolution_lab.ts +53 -1
- package/src/scripts/generate_capability_matrix.ts +16 -9
- package/src/scripts/git_authorization_hook.ts +476 -6
- package/src/scripts/grade_target_readiness.ts +221 -2
- package/src/scripts/hook_effect_doctor.ts +47 -4
- package/src/scripts/hooks/block_unauthorized_git.ts +238 -9
- package/src/scripts/hooks/merge_impact.ts +207 -0
- package/src/scripts/hooks/run_continuation_hook.ts +78 -88
- package/src/scripts/hooks/session_eol_hook.ts +12 -1
- package/src/scripts/install-hooks.sh +18 -0
- package/src/scripts/lint_code_comments.ts +106 -1
- package/src/scripts/release.ts +5 -17
- package/src/scripts/release_drill.ts +76 -1
- package/src/scripts/release_env.ts +31 -0
- package/src/scripts/release_publication.ts +125 -1
- package/src/scripts/roadmap_context.ts +15 -0
- package/src/scripts/skill_scout.ts +633 -0
- package/src/scripts/sync_pr_branch.ts +521 -42
|
@@ -122,6 +122,20 @@ interface WorldConfig {
|
|
|
122
122
|
* its own, because a marker otherwise stops the run at step 8.
|
|
123
123
|
*/
|
|
124
124
|
tag_already_remote?: boolean;
|
|
125
|
+
/**
|
|
126
|
+
* The release PR is ALREADY merged when the run starts — the real
|
|
127
|
+
* resume-after-merge state, in which steps 1 through 7 skip and step 8 is
|
|
128
|
+
* the first step that does work.
|
|
129
|
+
*
|
|
130
|
+
* Added 2026-09-02 with `guard_release_branch_push`. Before that guard, a
|
|
131
|
+
* marker scenario could start from scratch and still reach step 8, because
|
|
132
|
+
* nothing between the changelog write and the tag looked at the section.
|
|
133
|
+
* The push guard now stops such a run at step 4 — correctly, and that is
|
|
134
|
+
* the point of it — which makes this knob the only way left to exercise
|
|
135
|
+
* the tag and Release guards on their own. It is not a bypass: the state
|
|
136
|
+
* it fakes is one every resumed release actually passes through.
|
|
137
|
+
*/
|
|
138
|
+
pr_already_merged?: boolean;
|
|
125
139
|
}
|
|
126
140
|
|
|
127
141
|
/**
|
|
@@ -206,6 +220,7 @@ class FakeWorld {
|
|
|
206
220
|
this.merge_races_once = cfg.merge_races_once ?? false;
|
|
207
221
|
this.merge_fails_hard = cfg.merge_fails_hard ?? false;
|
|
208
222
|
this.checks_fail = cfg.checks_fail ?? false;
|
|
223
|
+
this.pr_merged = cfg.pr_already_merged ?? false;
|
|
209
224
|
}
|
|
210
225
|
|
|
211
226
|
exec(args: readonly string[]): ExecResult {
|
|
@@ -375,10 +390,68 @@ function _count(world: FakeWorld, needle: string): number {
|
|
|
375
390
|
}
|
|
376
391
|
|
|
377
392
|
const SCENARIOS: Record<string, Scenario> = {
|
|
393
|
+
'marker-refuses-before-commit': {
|
|
394
|
+
summary:
|
|
395
|
+
'step 3: a draft marker refuses BEFORE the release commit — no local release state, and therefore no remote state either',
|
|
396
|
+
config: { changelog_file: markedChangelogFixture(current_version()) },
|
|
397
|
+
expect_success: false,
|
|
398
|
+
verify: (w, err) => {
|
|
399
|
+
const f: string[] = [];
|
|
400
|
+
_expect(
|
|
401
|
+
(err ?? '').includes('release highlights are still the generator'),
|
|
402
|
+
`died for the wrong reason: ${err ?? '(no error)'}`,
|
|
403
|
+
f,
|
|
404
|
+
);
|
|
405
|
+
_expect(
|
|
406
|
+
!w.calls.some((c) => c.startsWith('git commit')),
|
|
407
|
+
'a release commit was made after the refusal',
|
|
408
|
+
f,
|
|
409
|
+
);
|
|
410
|
+
// RENAMED from `marker-refuses-before-branch-push` when
|
|
411
|
+
// `guard_release_curation` moved the refusal one step earlier.
|
|
412
|
+
// Every negative assertion below is UNCHANGED and still
|
|
413
|
+
// load-bearing — each is a remote side effect the 14.14.0 run
|
|
414
|
+
// actually produced: a pushed branch, an open PR, a check watch on
|
|
415
|
+
// a check that could not pass. They now hold more strongly, since
|
|
416
|
+
// the run stops before the commit rather than after it, and the
|
|
417
|
+
// no-commit assertion above is the property that is new.
|
|
418
|
+
//
|
|
419
|
+
// What this scenario no longer reaches, said plainly rather than
|
|
420
|
+
// left for a reader to infer: `guard_release_branch_push`. On the
|
|
421
|
+
// non-merged path it is now unreachable in ONE run, because a
|
|
422
|
+
// section marked at step 4 was already marked at step 3. It stays
|
|
423
|
+
// as defence-in-depth for a section that changes BETWEEN the two —
|
|
424
|
+
// a hand edit mid-run, or a resume over an altered changelog — and
|
|
425
|
+
// its own behaviour stays pinned by the unit tests in
|
|
426
|
+
// `tests/scripts/release_push_failure_masking.test.ts`, which is
|
|
427
|
+
// where that coverage moved to rather than being dropped.
|
|
428
|
+
_expect(
|
|
429
|
+
!w.calls.includes(`git push -u origin ${w.branch}`),
|
|
430
|
+
'the branch was pushed after the refusal',
|
|
431
|
+
f,
|
|
432
|
+
);
|
|
433
|
+
_expect(
|
|
434
|
+
!w.calls.some((c) => c.startsWith('gh pr create')),
|
|
435
|
+
'a pull request was opened after the refusal',
|
|
436
|
+
f,
|
|
437
|
+
);
|
|
438
|
+
_expect(
|
|
439
|
+
!w.calls.some((c) => c.includes('--watch')),
|
|
440
|
+
'CI was waited on after the refusal',
|
|
441
|
+
f,
|
|
442
|
+
);
|
|
443
|
+
_expect(!w.tag_local && !w.tag_remote, 'a tag exists after the refusal', f);
|
|
444
|
+
_expect(!w.release_created, 'a GitHub Release was created after the refusal', f);
|
|
445
|
+
return f;
|
|
446
|
+
},
|
|
447
|
+
},
|
|
378
448
|
'marker-refuses-before-tag': {
|
|
379
449
|
summary:
|
|
380
450
|
'step 8: a draft marker in the merged section refuses BEFORE the annotated tag is created',
|
|
381
|
-
config: {
|
|
451
|
+
config: {
|
|
452
|
+
pr_already_merged: true,
|
|
453
|
+
changelog_file: markedChangelogFixture(current_version()),
|
|
454
|
+
},
|
|
382
455
|
expect_success: false,
|
|
383
456
|
verify: (w, err) => {
|
|
384
457
|
const f: string[] = [];
|
|
@@ -403,6 +476,7 @@ const SCENARIOS: Record<string, Scenario> = {
|
|
|
403
476
|
summary:
|
|
404
477
|
'step 8 (§ 3.2): a tag created but never pushed is NOT pushed when its section carries the marker',
|
|
405
478
|
config: {
|
|
479
|
+
pr_already_merged: true,
|
|
406
480
|
tag_created_unpushed: true,
|
|
407
481
|
changelog_file: markedChangelogFixture(current_version()),
|
|
408
482
|
},
|
|
@@ -431,6 +505,7 @@ const SCENARIOS: Record<string, Scenario> = {
|
|
|
431
505
|
summary:
|
|
432
506
|
'step 9: with the tag already pushed, a marker in the TAGGED section refuses before the Release is created',
|
|
433
507
|
config: {
|
|
508
|
+
pr_already_merged: true,
|
|
434
509
|
tag_already_remote: true,
|
|
435
510
|
changelog: markedChangelogFixture(current_version()),
|
|
436
511
|
changelog_file: markedChangelogFixture(current_version()),
|
|
@@ -303,6 +303,35 @@ function set_marketplace_version(p: string, version: string): void {
|
|
|
303
303
|
fs.writeFileSync(p, jsonDumpsIndent(data, 2) + '\n', 'utf-8');
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Mirror of Python `str.splitlines()` — split on CR/LF/CRLF and drop a single
|
|
308
|
+
* trailing empty element.
|
|
309
|
+
*
|
|
310
|
+
* Lives here rather than in `release.ts` because that file is 2000+ lines and
|
|
311
|
+
* every line above 1500 is charged by `check_source_size_budget`, while this
|
|
312
|
+
* module is a third of the cap. Behaviour unchanged; it is a pure text helper
|
|
313
|
+
* beside the other pure text helpers in this module.
|
|
314
|
+
*
|
|
315
|
+
* Known duplication, recorded rather than fixed here: `adoption_report.ts:106`
|
|
316
|
+
* and `readme_linter.ts:418` carry their own copies. Collapsing all three onto
|
|
317
|
+
* one definition touches three modules and is not this change's subject.
|
|
318
|
+
*/
|
|
319
|
+
function _splitlines(text: string): string[] {
|
|
320
|
+
if (text === '') {
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
const parts = text.split(/\r\n|\r|\n/);
|
|
324
|
+
if (parts.length > 0 && parts[parts.length - 1] === '') {
|
|
325
|
+
parts.pop();
|
|
326
|
+
}
|
|
327
|
+
return parts;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Mirror of Python `str.rstrip()` (trailing whitespace). */
|
|
331
|
+
function _rstrip(text: string): string {
|
|
332
|
+
return text.replace(/\s+$/u, '');
|
|
333
|
+
}
|
|
334
|
+
|
|
306
335
|
export {
|
|
307
336
|
ArgparseExit,
|
|
308
337
|
commaGroup,
|
|
@@ -330,4 +359,6 @@ export {
|
|
|
330
359
|
REPO_ROOT,
|
|
331
360
|
REPO_SLUG,
|
|
332
361
|
SystemExitError,
|
|
362
|
+
_rstrip,
|
|
363
|
+
_splitlines,
|
|
333
364
|
};
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* import path changes.
|
|
17
17
|
*/
|
|
18
18
|
import { spawnSync } from 'node:child_process';
|
|
19
|
-
import { publication_blockers } from './_lib/release_highlights.js';
|
|
19
|
+
import { DERIVED_MARKER, publication_blockers } from './_lib/release_highlights.js';
|
|
20
20
|
import * as fs from 'node:fs';
|
|
21
21
|
import process from 'node:process';
|
|
22
22
|
|
|
@@ -422,7 +422,131 @@ export function _is_non_fast_forward(stderr: string, stdout: string): boolean {
|
|
|
422
422
|
return /(non-fast-forward|fetch first|behind its remote counterpart)/i.test(text);
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
/**
|
|
426
|
+
* Refuse the FIRST remote state of a release whose section is not publishable.
|
|
427
|
+
*
|
|
428
|
+
* The fourth guard site, and the one the other three structurally cannot cover.
|
|
429
|
+
* They sit at the irreversible transitions — annotated tag, resumed tag push,
|
|
430
|
+
* GitHub Release body — and every one of them is DOWNSTREAM of `gh pr create`
|
|
431
|
+
* and of `gh pr checks --watch`. `check_release_highlights` refuses the same
|
|
432
|
+
* section from the other side, on the release PR, so a run whose head is still
|
|
433
|
+
* the generator's draft pushed a branch, opened a PR, burned CI, waited on a
|
|
434
|
+
* check that could not pass, and died there — leaving a half-open release to
|
|
435
|
+
* clean up by hand.
|
|
436
|
+
*
|
|
437
|
+
* That is not a hypothetical: it is PR #1812 (14.14.0, 2026-09-02), and the
|
|
438
|
+
* 2026-09-01 flip's own risk register pre-registered it as Risk 1 — *"the flip
|
|
439
|
+
* reds the next release PR on its first run"*. Its stated mitigation was that
|
|
440
|
+
* the releaser edits prose before merge; nothing in the pipeline asked, so
|
|
441
|
+
* nothing did.
|
|
442
|
+
*
|
|
443
|
+
* The branch push is the first thing in the pipeline that leaves the machine,
|
|
444
|
+
* so a refusal here costs one local edit and no remote state at all: no branch
|
|
445
|
+
* on the remote, no pull request, no CI minutes, no tag. It sits at the push
|
|
446
|
+
* CALL SITE rather than in a formatter for the reason the publication guards
|
|
447
|
+
* already state — a formatter has no notion of whether it is publishing, and
|
|
448
|
+
* the call site is the one that can still stop. Deriving the version from
|
|
449
|
+
* `release/X.Y.Z` mirrors what `release-validation.yml` already does with
|
|
450
|
+
* `head_ref`.
|
|
451
|
+
*
|
|
452
|
+
* Two states pass unguarded, deliberately: a branch that is not
|
|
453
|
+
* `release/X.Y.Z` (nothing about a release to read), and a version with no
|
|
454
|
+
* changelog section (the missing-section refusal belongs to the publication
|
|
455
|
+
* sites, which need a section to publish; refusing here would break every
|
|
456
|
+
* non-release push through this helper).
|
|
457
|
+
*/
|
|
458
|
+
export function guard_release_branch_push(branch: string): void {
|
|
459
|
+
const prefix = 'release/';
|
|
460
|
+
if (!branch.startsWith(prefix)) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const version = branch.slice(prefix.length);
|
|
464
|
+
const section = extract_changelog_section(read_changelog_text(), version);
|
|
465
|
+
if (!section) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const blockers = publication_blockers(section.body, version, `\`${branch}\``);
|
|
469
|
+
if (blockers.length === 0) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
die(
|
|
473
|
+
`refusing to push ${branch} — the ${version} changelog section is not publishable ` +
|
|
474
|
+
`(${String(blockers.length)} blocker(s)):\n` +
|
|
475
|
+
blockers.map((b) => ` - ${b}`).join('\n') +
|
|
476
|
+
`\n Nothing has left this machine: no branch on ${REMOTE}, no pull request, no tag.\n` +
|
|
477
|
+
' Curate the `### Release highlights` head in CHANGELOG.md under ' +
|
|
478
|
+
`\`## [${version}]\`, then re-run \`task release -- --resume\`.`,
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Ask for the curation BEFORE the pipeline commits anything — the fifth guard
|
|
484
|
+
* site, and the one that closes the gap the other four only narrowed.
|
|
485
|
+
*
|
|
486
|
+
* `guard_release_branch_push` already refuses before any REMOTE state exists,
|
|
487
|
+
* which is why 14.14.1 cost nothing but a re-run. What it cannot do is refuse
|
|
488
|
+
* before LOCAL state exists: by the time the push is attempted, step 2 has
|
|
489
|
+
* bumped six files and regenerated the derived trees and step 3 has committed
|
|
490
|
+
* all of it as `release: X.Y.Z`. The operator then edits prose on top of a
|
|
491
|
+
* commit that already claims to be the release and continues with `--resume`.
|
|
492
|
+
*
|
|
493
|
+
* The sentence this fixes is in that guard's own docstring: the 2026-09-01
|
|
494
|
+
* flip's risk register named *"the releaser edits prose before merge"* as the
|
|
495
|
+
* mitigation, and *"nothing in the pipeline asked, so nothing did"*. Refusing
|
|
496
|
+
* is not asking. This is the ask, placed where the answer is cheapest: the
|
|
497
|
+
* changelog section has just been written, nothing is committed, and the fix is
|
|
498
|
+
* an edit to a file already open in the working tree.
|
|
499
|
+
*
|
|
500
|
+
* It names the labels rather than only the marker, because the operator's next
|
|
501
|
+
* action is to rewrite specific lines and hunting for them in a 30-line section
|
|
502
|
+
* is the friction that made the refusal feel like a failure rather than a step.
|
|
503
|
+
*
|
|
504
|
+
* Deliberately silent when the section does not exist — same reasoning as the
|
|
505
|
+
* push guard: a missing section is the publication sites' refusal to make, not
|
|
506
|
+
* this one's.
|
|
507
|
+
*
|
|
508
|
+
* `prMerged` is a parameter rather than a check at the call site, and it is
|
|
509
|
+
* load-bearing in both directions. On the merged path steps 2 and 3 do not run,
|
|
510
|
+
* so there is no pre-commit moment to ask about; firing anyway PREEMPTS the
|
|
511
|
+
* step-8 and step-9 guards, whose whole subject is the merged section. That is
|
|
512
|
+
* measured, not anticipated — the unconditional form red three `release_drill`
|
|
513
|
+
* scenarios that exist to pin exactly those later refusals. It lives here
|
|
514
|
+
* rather than in an `if` at the call site because `release.ts` is 2000+ lines
|
|
515
|
+
* and every line above 1500 is charged by the growth ratchet, while this module
|
|
516
|
+
* is far below it.
|
|
517
|
+
*/
|
|
518
|
+
export function guard_release_curation(version: string, prMerged = false): void {
|
|
519
|
+
if (prMerged) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const section = extract_changelog_section(read_changelog_text(), version);
|
|
523
|
+
if (!section) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const blockers = publication_blockers(section.body, version, `\`release/${version}\``);
|
|
527
|
+
if (blockers.length === 0) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const marked = section.body
|
|
531
|
+
.split('\n')
|
|
532
|
+
.filter((l) => l.includes(DERIVED_MARKER))
|
|
533
|
+
.map((l) => ` ${l.trim()}`);
|
|
534
|
+
die(
|
|
535
|
+
`the ${version} release highlights are still the generator's draft ` +
|
|
536
|
+
`(${String(blockers.length)} blocker(s)):\n` +
|
|
537
|
+
blockers.map((b) => ` - ${b}`).join('\n') +
|
|
538
|
+
(marked.length > 0
|
|
539
|
+
? `\n Lines to rewrite in \`CHANGELOG.md\` under \`## [${version}]\`:\n` +
|
|
540
|
+
marked.join('\n')
|
|
541
|
+
: '') +
|
|
542
|
+
'\n Stopped BEFORE committing — no release commit, no branch on ' +
|
|
543
|
+
`${REMOTE}, no pull request, no tag. Curate the ` +
|
|
544
|
+
'`### Release highlights` head, then re-run `task release`.',
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
|
|
425
548
|
function push_release_branch(branch: string): void {
|
|
549
|
+
guard_release_branch_push(branch);
|
|
426
550
|
const first = run(['git', 'push', '-u', REMOTE, branch], { check: false, capture: true });
|
|
427
551
|
if (first.returncode === 0) {
|
|
428
552
|
process.stdout.write(first.stdout);
|
|
@@ -49,6 +49,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
49
49
|
import { gitEnv } from './_lib/git_env.js';
|
|
50
50
|
import { read_live_records, register_dir } from './_lib/session_register.js';
|
|
51
51
|
import { other_worktree_branches_detailed } from './sessions_cli.js';
|
|
52
|
+
import { recordContextObservation } from './_lib/context_observation.js';
|
|
52
53
|
|
|
53
54
|
/** One open pull request, with the file set it changes. */
|
|
54
55
|
export interface PullRequestInfo {
|
|
@@ -746,6 +747,20 @@ export function main(argv: string[] = process.argv.slice(2)): number {
|
|
|
746
747
|
roadmap: at('--roadmap'),
|
|
747
748
|
...(ownedFile !== null ? { ownedPaths: readOwnedPathsFile(ownedFile) } : {}),
|
|
748
749
|
});
|
|
750
|
+
// `road-to-wired-instruments` 2.1 — this probe is the only thing in the tree
|
|
751
|
+
// that can OBSERVE the world (it costs a `gh` call), so it records what it
|
|
752
|
+
// saw where a Stop-path concern can read it without spawning anything. The
|
|
753
|
+
// continuation ladder's premise rung is the consumer; the resume checkpoint
|
|
754
|
+
// is the other.
|
|
755
|
+
//
|
|
756
|
+
// Only a LIVE reading is recorded. Offline, `contextFingerprint` still
|
|
757
|
+
// returns a digest — over a null base and an empty PR list — and that digest
|
|
758
|
+
// is a statement about the network, not about the repository. Recording it
|
|
759
|
+
// would make every offline probe look like a moved premise, which is a false
|
|
760
|
+
// halt manufactured out of a dropped connection.
|
|
761
|
+
if (ctx.network === 'live') {
|
|
762
|
+
recordContextObservation(root, ctx.roadmap ?? '', ctx.fingerprint);
|
|
763
|
+
}
|
|
749
764
|
if (argv.includes('--fingerprint')) {
|
|
750
765
|
// Just the digest, for the loop's phase-boundary comparison and for the
|
|
751
766
|
// resume checkpoint's `context_fingerprint` (§ 5.6 — same value).
|