@event4u/agent-config 14.19.0 → 14.21.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 +351 -156
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +10 -0
- package/dist/agent-src/commands/agent-handoff.md +42 -41
- package/dist/agent-src/commands/analyze/inbox.md +1 -1
- package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +247 -32
- package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
- package/dist/agent-src/commands/analyze.md +12 -7
- package/dist/agent-src/commands/feature/plan.md +55 -16
- package/dist/agent-src/commands/feature/refactor.md +8 -3
- package/dist/agent-src/commands/humanize.md +48 -4
- package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +11 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
- package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
- package/dist/agent-src/rules/evaluator-independence.md +4 -4
- package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +0 -2
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
- package/dist/agent-src/skills/evaluate-llm-feature/SKILL.md +1 -1
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +2 -2
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +28 -6
- package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -3
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/features.md +5 -0
- package/dist/agent-src/templates/roadmaps.md +29 -1
- package/dist/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
- package/dist/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
- package/dist/agent-src/templates/scripts/telemetry_report.ts +22 -2
- package/dist/cli/mcp/dispatch.js +10 -0
- package/dist/cli/mcp/stdio.js +33 -2
- package/dist/cli/mcp/telemetry.js +227 -0
- package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
- package/dist/cli-delegate/{chunk-PMEKGDED.js → chunk-2TAQ2K4V.js} +166 -1
- package/dist/cli-delegate/{chunk-GZTMRYCN.js → chunk-643Z7FIC.js} +79 -12
- package/dist/cli-delegate/{chunk-CYH5SP6M.js → chunk-I6RHFQEG.js} +151 -16
- package/dist/cli-delegate/{chunk-IDHUQIXP.js → chunk-J3RFNKG4.js} +41 -5
- package/dist/cli-delegate/{chunk-7PRE6ZV2.js → chunk-YS6XTLEK.js} +199 -4
- package/dist/cli-delegate/chunk-ZVBOHSVF.js +1109 -0
- package/dist/cli-delegate/cmd_conformance.js +88 -110
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +42 -12
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +59 -902
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +62 -31
- 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 +7 -6
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -5
- package/dist/hooks/dispatch.js +3801 -2803
- package/dist/install/install.mjs +396 -168
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +809 -180
- package/dist/scripts/ai_council/content_ceiling.js +63 -0
- package/dist/scripts/ai_council/seat_policy.js +106 -0
- package/dist/scripts/ai_council/transport_resolver.js +21 -0
- package/dist/scripts/mcp_server/telemetry.js +148 -0
- package/docs/guidelines/agent-infra/naming.md +1 -1
- package/docs/guidelines/agent-infra/security-lint-containment.md +46 -6
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +4 -4
- package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +1 -1
- package/docs/guidelines/redundancy-taxonomy.md +28 -0
- package/llms.txt +14 -14
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
- package/src/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
- package/src/agent-src/templates/scripts/telemetry_report.ts +22 -2
- package/src/config/assurance-capability-registry.json +9 -0
- package/src/config/gate-coverage.yml +55 -0
- package/src/config/gate-violation-baselines.json +18 -6
- package/src/config/release-gate-locality.yml +4 -3
- package/src/scripts/_cli/cmd_session_recycle.ts +51 -3
- package/src/scripts/_lib/audit_field_provenance.ts +44 -3
- package/src/scripts/_lib/blocked_by_marker.ts +189 -0
- package/src/scripts/_lib/host_capability.ts +76 -1
- package/src/scripts/_lib/install_reach_checks.ts +33 -1
- package/src/scripts/_lib/mcp_bridge.ts +222 -10
- package/src/scripts/_lib/mcp_consent_residual.ts +173 -0
- package/src/scripts/_lib/orchestration_record.ts +23 -1
- package/src/scripts/_lib/recycle_envelope_paths.ts +190 -0
- package/src/scripts/_lib/release_highlights.ts +19 -211
- package/src/scripts/_lib/release_material.ts +27 -77
- package/src/scripts/_lib/release_position.ts +96 -0
- package/src/scripts/_lib/review_skipped_record.ts +17 -1
- package/src/scripts/_lib/security_lint.ts +179 -7
- package/src/scripts/_lib/self_repair.ts +141 -6
- package/src/scripts/_lib/self_repair_store.ts +273 -7
- package/src/scripts/_lib/structured_ask.ts +117 -0
- package/src/scripts/_lib/subagent_capsule.ts +155 -3
- package/src/scripts/_lib/tty_prompt.ts +76 -0
- package/src/scripts/ai_council/api_transport.ts +163 -0
- package/src/scripts/ai_council/clients.ts +58 -63
- package/src/scripts/ai_council/config.ts +13 -0
- package/src/scripts/ai_council/content_ceiling.ts +79 -0
- package/src/scripts/ai_council/jury_aggregate.ts +178 -0
- package/src/scripts/ai_council/seat_policy.ts +137 -0
- package/src/scripts/ai_council/status_surface.ts +79 -0
- package/src/scripts/ai_council/transport_resolver.ts +59 -1
- package/src/scripts/ai_tells_rules.ts +446 -5
- package/src/scripts/ask_block_census.ts +499 -0
- package/src/scripts/bench_humanizer_eval.ts +264 -40
- package/src/scripts/bench_prose_tells_fp.ts +198 -0
- package/src/scripts/build_rule_trigger_matrix.ts +5 -0
- package/src/scripts/check_branch_work_committed.ts +234 -0
- package/src/scripts/check_mcp_doc_drift.ts +217 -0
- package/src/scripts/check_pack_size.ts +321 -3
- package/src/scripts/check_release_highlights.ts +0 -157
- package/src/scripts/council_cli.ts +18 -27
- package/src/scripts/detect_ai_tells.ts +142 -20
- package/src/scripts/handoff_context_hook.ts +54 -7
- package/src/scripts/harvest_reference_tokens.ts +698 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +31 -1
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
- package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
- package/src/scripts/hooks/session_eol_hook.ts +18 -7
- package/src/scripts/install-hooks.sh +52 -0
- package/src/scripts/install.ts +4 -4
- package/src/scripts/lint_agent_security.ts +316 -25
- package/src/scripts/lint_confusables.ts +42 -6
- package/src/scripts/lint_handoffs.ts +0 -132
- package/src/scripts/lint_hidden_unicode.ts +49 -7
- package/src/scripts/lint_instruction_smuggling.ts +42 -6
- package/src/scripts/lint_mcp_config_security.ts +42 -6
- package/src/scripts/lint_roadmap_blockers.ts +51 -1
- package/src/scripts/lint_roadmap_later_disposition.ts +217 -27
- package/src/scripts/lint_skill_frontmatter_safety.ts +43 -7
- package/src/scripts/mcp_server/resources.ts +41 -0
- package/src/scripts/mcp_server/telemetry.ts +19 -4
- package/src/scripts/migrate_command_suggestions.ts +1 -1
- package/src/scripts/orchestration_record.ts +12 -1
- package/src/scripts/probe_unblocked_ask.ts +135 -2
- package/src/scripts/release.ts +50 -95
- package/src/scripts/release_drill.ts +6 -10
- package/src/scripts/release_env.ts +1 -0
- package/src/scripts/release_publication.ts +40 -31
- package/src/scripts/self_repair_cli.ts +31 -1
- package/src/scripts/self_review_gate.ts +409 -15
- package/src/scripts/skill_scout.ts +69 -0
- package/dist/cli-delegate/chunk-2GNWGMAL.js +0 -172
- package/dist/cli-delegate/chunk-LPPWO5CE.js +0 -161
|
@@ -25,15 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { spawnSync } from 'node:child_process';
|
|
27
27
|
|
|
28
|
-
import {
|
|
29
|
-
CURATED_HEAD_INSTRUCTION,
|
|
30
|
-
MIX_RESPONSE_MARKER,
|
|
31
|
-
MIX_RESPONSE_PLACEHOLDERS,
|
|
32
|
-
PROMISE_OUTCOMES,
|
|
33
|
-
PROMISE_PHRASE,
|
|
34
|
-
PROMISE_READBACK_MARKER,
|
|
35
|
-
extract_changelog_section,
|
|
36
|
-
} from './release_material.js';
|
|
28
|
+
import { CURATED_HEAD_INSTRUCTION } from './release_material.js';
|
|
37
29
|
|
|
38
30
|
/** The five curated labels, in the order an operator reads them. */
|
|
39
31
|
export const HEAD_LABELS: readonly string[] = [
|
|
@@ -406,7 +398,6 @@ export function publication_blockers(
|
|
|
406
398
|
sectionBody: string,
|
|
407
399
|
version: string,
|
|
408
400
|
where = '`main`',
|
|
409
|
-
mix: MixObligation | null = null,
|
|
410
401
|
): string[] {
|
|
411
402
|
const out: string[] = [];
|
|
412
403
|
if (sectionBody.includes(DERIVED_MARKER)) {
|
|
@@ -423,13 +414,15 @@ export function publication_blockers(
|
|
|
423
414
|
'comment line from the section and re-run.',
|
|
424
415
|
);
|
|
425
416
|
}
|
|
426
|
-
out.push(...mix_response_blockers(sectionBody, version, where, mix));
|
|
427
417
|
return out;
|
|
428
418
|
}
|
|
429
419
|
|
|
430
420
|
/**
|
|
431
421
|
* Everything that makes a whole SECTION unpublishable — the head-level
|
|
432
|
-
* blockers above plus the
|
|
422
|
+
* blockers above plus the tests footer, which only exists for a full section.
|
|
423
|
+
*
|
|
424
|
+
* It was "the two obligations" until 2026-09-07: the governance-versus-product
|
|
425
|
+
* written answer was the other, and ADR-261 deleted it. One remains.
|
|
433
426
|
*
|
|
434
427
|
* Two levels and not one, because `publication_blockers` is legitimately called
|
|
435
428
|
* with a bare curated head (the prefill tests do exactly that), and a head
|
|
@@ -450,9 +443,8 @@ export function section_publication_blockers(
|
|
|
450
443
|
sectionBody: string,
|
|
451
444
|
version: string,
|
|
452
445
|
where = '`main`',
|
|
453
|
-
mix: MixObligation | null = null,
|
|
454
446
|
): string[] {
|
|
455
|
-
const out = publication_blockers(sectionBody, version, where
|
|
447
|
+
const out = publication_blockers(sectionBody, version, where);
|
|
456
448
|
if (!TEST_FOOTER_RE.test(sectionBody)) {
|
|
457
449
|
out.push(
|
|
458
450
|
`the ${version} section carries no \`Tests: N (+M since PREV)\` footer. ` +
|
|
@@ -479,208 +471,24 @@ export function section_publication_blockers(
|
|
|
479
471
|
const TEST_FOOTER_RE = /^Tests: \d+/mu;
|
|
480
472
|
|
|
481
473
|
/**
|
|
482
|
-
* The measured governance-versus-product level, as the writer
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
* `
|
|
474
|
+
* The measured governance-versus-product level, as the writer receives it.
|
|
475
|
+
*
|
|
476
|
+
* A plain record and not the measurement itself: only the callers have git to
|
|
477
|
+
* measure with, and keeping `measure_release_mix`'s git access out of this
|
|
478
|
+
* module keeps it importable by the guards.
|
|
479
|
+
*
|
|
480
|
+
* It carried a `triggered` boolean until ADR-261, when the obligation that
|
|
481
|
+
* boolean gated was deleted. It was removed rather than kept with a rationale:
|
|
482
|
+
* the first attempt at this docblock claimed a reader of the published line
|
|
483
|
+
* could see the verdict, and a neutral review falsified that — `triggered` was
|
|
484
|
+
* set once and read nowhere, and `render_mix_response` never received it. The
|
|
485
|
+
* two counts in `level` are what the line publishes, and the taxonomy's own
|
|
486
|
+
* verdict stays where it is computed and printed, in `measure_release_mix`.
|
|
489
487
|
*/
|
|
490
488
|
export interface MixObligation {
|
|
491
|
-
triggered: boolean;
|
|
492
489
|
level: string;
|
|
493
490
|
}
|
|
494
491
|
|
|
495
|
-
/** Minimum written characters after the marker — a bare marker is not an answer. */
|
|
496
|
-
export const MIX_RESPONSE_MIN_CHARS = 40;
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* The ONE predicate for the governance-versus-product obligation.
|
|
500
|
-
*
|
|
501
|
-
* `check_release_highlights` refuses this from the CI side and
|
|
502
|
-
* `guard_release_branch_push` from the local side, and until 2026-09-05 only
|
|
503
|
-
* the first of the two knew the obligation existed. That asymmetry is what the
|
|
504
|
-
* contract itself recorded — *"the earliest refusal for this one obligation is
|
|
505
|
-
* the PR, not the push"* (`docs/contracts/CHANGELOG-conventions.md`) — and what
|
|
506
|
-
* cost 14.17.0 a red release PR. Both sides now read this function, so a change
|
|
507
|
-
* to the obligation cannot reach one guard and miss the other.
|
|
508
|
-
*
|
|
509
|
-
* `null` means the measurement did not run (shallow clone, missing tag). That
|
|
510
|
-
* degrades to no blocker, matching the gate's documented stance: this is a
|
|
511
|
-
* governance signal, not a correctness control, and an environment fact must
|
|
512
|
-
* not block a release.
|
|
513
|
-
*/
|
|
514
|
-
export function mix_response_blockers(
|
|
515
|
-
sectionBody: string,
|
|
516
|
-
version: string,
|
|
517
|
-
where = '`main`',
|
|
518
|
-
mix: MixObligation | null = null,
|
|
519
|
-
): string[] {
|
|
520
|
-
if (!mix || !mix.triggered) {
|
|
521
|
-
return [];
|
|
522
|
-
}
|
|
523
|
-
const idx = sectionBody.indexOf(MIX_RESPONSE_MARKER);
|
|
524
|
-
if (idx === -1) {
|
|
525
|
-
return [
|
|
526
|
-
`the ${version} section owes a governance-versus-product response ` +
|
|
527
|
-
`(${mix.level}) and carries none. Add one \`> ${MIX_RESPONSE_MARKER}\` line ` +
|
|
528
|
-
`immediately under the curated head on ${where}, naming the next cycle's ` +
|
|
529
|
-
'consumer work or a maintainer justification.',
|
|
530
|
-
];
|
|
531
|
-
}
|
|
532
|
-
const block = mix_response_block(sectionBody, idx);
|
|
533
|
-
const left = MIX_RESPONSE_PLACEHOLDERS.filter((t) => block.includes(t));
|
|
534
|
-
if (left.length > 0) {
|
|
535
|
-
return [
|
|
536
|
-
`the ${version} governance-versus-product response still carries the writer's ` +
|
|
537
|
-
`placeholder(s) (${left.map((t) => `\`${t}\``).join(', ')}) — the measured level ` +
|
|
538
|
-
'is the generator\'s, the answer is not, and `CHANGELOG.md` is published to npm. ' +
|
|
539
|
-
`Name the next cycle's consumer work (or the justification) on ${where} and re-run.`,
|
|
540
|
-
];
|
|
541
|
-
}
|
|
542
|
-
if (human_answer(block, mix.level).length < MIX_RESPONSE_MIN_CHARS) {
|
|
543
|
-
return [
|
|
544
|
-
`the ${version} governance-versus-product response carries no written answer ` +
|
|
545
|
-
`beyond the measured level (${mix.level}). The level is the generator's; the ` +
|
|
546
|
-
`response has to name the next cycle's consumer work or a maintainer ` +
|
|
547
|
-
`justification. Write it on ${where}, in the \`> \` block under the marker.`,
|
|
548
|
-
];
|
|
549
|
-
}
|
|
550
|
-
return [];
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* The promise the PREVIOUS release head made, or null when it made none.
|
|
555
|
-
*
|
|
556
|
-
* Scoped to that section's governance-response block, not to its whole body: a
|
|
557
|
-
* product line that happens to contain the phrase is not a promise the project
|
|
558
|
-
* made about the next cycle.
|
|
559
|
-
*/
|
|
560
|
-
export function previous_promise(changelogText: string, previousVersion: string): string | null {
|
|
561
|
-
const section = extract_changelog_section(changelogText, previousVersion);
|
|
562
|
-
if (section === null) return null;
|
|
563
|
-
const idx = section.body.indexOf(MIX_RESPONSE_MARKER);
|
|
564
|
-
if (idx === -1) return null;
|
|
565
|
-
const block = mix_response_block(section.body, idx);
|
|
566
|
-
return block.includes(PROMISE_PHRASE) ? block : null;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* The ONE predicate for "did this head answer the previous head's promise?".
|
|
571
|
-
*
|
|
572
|
-
* The defect: `_lib/release_material.ts` generates a `Next cycle ships …` line
|
|
573
|
-
* and nothing has ever read the previous one back, so an unmet promise can be
|
|
574
|
-
* restated indefinitely at zero cost — the debt is booked and never called. A
|
|
575
|
-
* promise nobody reads is not a commitment, it is a sentence.
|
|
576
|
-
*
|
|
577
|
-
* Three outcomes are accepted and one of them must be chosen, which is the
|
|
578
|
-
* whole mechanism: it does not require the promise to have been KEPT, and no
|
|
579
|
-
* ratio, deadline or count is enforced here. Refusing an unkept promise would
|
|
580
|
-
* make the honest answer the expensive one and teach an author to promise less
|
|
581
|
-
* rather than to answer truthfully.
|
|
582
|
-
*
|
|
583
|
-
* Length floor and placeholder scan reuse the governance-response discipline
|
|
584
|
-
* for the same reason it exists there: a bare marker, or the writer's own
|
|
585
|
-
* scaffolding, is not an answer a human wrote.
|
|
586
|
-
*/
|
|
587
|
-
export function promise_readback_blockers(
|
|
588
|
-
sectionBody: string,
|
|
589
|
-
version: string,
|
|
590
|
-
previousVersion: string,
|
|
591
|
-
promise: string | null,
|
|
592
|
-
where = '`main`',
|
|
593
|
-
): string[] {
|
|
594
|
-
if (promise === null) return [];
|
|
595
|
-
const idx = sectionBody.indexOf(PROMISE_READBACK_MARKER);
|
|
596
|
-
if (idx === -1) {
|
|
597
|
-
return [
|
|
598
|
-
`the ${version} section does not answer the ${previousVersion} head's ` +
|
|
599
|
-
`\`${PROMISE_PHRASE}\` promise. Add one \`> ${PROMISE_READBACK_MARKER}\` line ` +
|
|
600
|
-
`immediately under the curated head on ${where}, naming the promise and whether ` +
|
|
601
|
-
`it ${PROMISE_OUTCOMES.join(', ')} (the last one with its reason).`,
|
|
602
|
-
];
|
|
603
|
-
}
|
|
604
|
-
const block = mix_response_block(sectionBody, idx);
|
|
605
|
-
const left = MIX_RESPONSE_PLACEHOLDERS.filter((t) => block.includes(t));
|
|
606
|
-
if (left.length > 0) {
|
|
607
|
-
return [
|
|
608
|
-
`the ${version} previous-cycle answer still carries the writer's placeholder(s) ` +
|
|
609
|
-
`(${left.map((t) => `\`${t}\``).join(', ')}). \`CHANGELOG.md\` is published to npm. ` +
|
|
610
|
-
`Write the outcome on ${where} and re-run.`,
|
|
611
|
-
];
|
|
612
|
-
}
|
|
613
|
-
const lower = block.toLowerCase();
|
|
614
|
-
const named = PROMISE_OUTCOMES.filter((o) => lower.includes(o));
|
|
615
|
-
if (named.length === 0) {
|
|
616
|
-
return [
|
|
617
|
-
`the ${version} previous-cycle answer names no outcome. It has to say which of ` +
|
|
618
|
-
`${PROMISE_OUTCOMES.join(', ')} applies to the ${previousVersion} promise — an ` +
|
|
619
|
-
'answer that restates the promise without resolving it is the failure this reads ' +
|
|
620
|
-
'back for.',
|
|
621
|
-
];
|
|
622
|
-
}
|
|
623
|
-
if (readback_answer(block).length < MIX_RESPONSE_MIN_CHARS) {
|
|
624
|
-
return [
|
|
625
|
-
`the ${version} previous-cycle answer is shorter than a written answer ` +
|
|
626
|
-
`(${String(MIX_RESPONSE_MIN_CHARS)} characters after the marker). Name what was ` +
|
|
627
|
-
`promised and what became of it on ${where}, not the outcome word alone.`,
|
|
628
|
-
];
|
|
629
|
-
}
|
|
630
|
-
return [];
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
/** What the author wrote under the read-back marker, marker and quoting removed. */
|
|
634
|
-
export function readback_answer(block: string): string {
|
|
635
|
-
return block
|
|
636
|
-
.replace(PROMISE_READBACK_MARKER, '')
|
|
637
|
-
.replace(/^[>\s.]+|[>\s.]+$/gu, '')
|
|
638
|
-
.replace(/[>\s]+/gu, ' ')
|
|
639
|
-
.trim();
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* The whole response block: the marker's line plus every blockquote line under it.
|
|
644
|
-
*
|
|
645
|
-
* Reading only the FIRST line after the marker was wrong in both directions, and
|
|
646
|
-
* both were reproduced before this was written. It **accepted** a section whose
|
|
647
|
-
* placeholder line had simply been deleted, because the machine-written level
|
|
648
|
-
* alone is 54 characters and cleared the floor — the smuggled auto-approval the
|
|
649
|
-
* placeholder exists to prevent. And it **refused** the writer's own two-line
|
|
650
|
-
* template, where the level sits on line 1 and the answer on line 2.
|
|
651
|
-
*
|
|
652
|
-
* Scoping to the block also fixes the placeholder scan, which searched the
|
|
653
|
-
* entire section body: a legitimate changelog line containing
|
|
654
|
-
* `<roadmap or issue>` blocked the release.
|
|
655
|
-
*/
|
|
656
|
-
export function mix_response_block(sectionBody: string, markerIdx: number): string {
|
|
657
|
-
const lines = sectionBody.slice(markerIdx).split('\n');
|
|
658
|
-
const out: string[] = [lines[0] ?? ''];
|
|
659
|
-
for (const line of lines.slice(1)) {
|
|
660
|
-
if (!line.trimStart().startsWith('>')) break;
|
|
661
|
-
out.push(line);
|
|
662
|
-
}
|
|
663
|
-
return out.join('\n');
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* What is left of the response block once the generator's own contribution is
|
|
668
|
-
* removed — i.e. the part a human had to write.
|
|
669
|
-
*
|
|
670
|
-
* The measured level is stripped because the writer emitted it; a floor that
|
|
671
|
-
* counted it would be satisfied by the tool on the author's behalf, which is
|
|
672
|
-
* the whole failure this obligation guards against.
|
|
673
|
-
*/
|
|
674
|
-
export function human_answer(block: string, level: string): string {
|
|
675
|
-
return block
|
|
676
|
-
.replace(MIX_RESPONSE_MARKER, '')
|
|
677
|
-
.split(level)
|
|
678
|
-
.join('')
|
|
679
|
-
.replace(/^[>\s.]+|[>\s.]+$/gu, '')
|
|
680
|
-
.replace(/[>\s]+/gu, ' ')
|
|
681
|
-
.trim();
|
|
682
|
-
}
|
|
683
|
-
|
|
684
492
|
/** Labels whose curated value is still the generator's unedited draft. */
|
|
685
493
|
export function stale_draft_labels(
|
|
686
494
|
curated: Readonly<Record<string, string>>,
|
|
@@ -300,95 +300,45 @@ function _first_diff(a: string, b: string): string {
|
|
|
300
300
|
* a branch, a PR and a CI run — the exact cost `guard_release_branch_push` was
|
|
301
301
|
* added to avoid for the sibling obligation two releases earlier.
|
|
302
302
|
*
|
|
303
|
-
* This module has no imports on purpose (see `CURATED_HEAD_INSTRUCTION`)
|
|
304
|
-
*
|
|
305
|
-
*
|
|
303
|
+
* This module has no imports on purpose (see `CURATED_HEAD_INSTRUCTION`).
|
|
304
|
+
*
|
|
305
|
+
* **One reader left, as of ADR-261 (2026-09-07).** This paragraph used to say
|
|
306
|
+
* the definition was shared by "the writer, the local push guard and the CI
|
|
307
|
+
* gate", which was true while `mix_response_blockers` and
|
|
308
|
+
* `check_governance_mix_response` scanned a section for this marker. Both are
|
|
309
|
+
* deleted, and with them every search for it: the only consumer now is
|
|
310
|
+
* `render_mix_response` in this same file, which WRITES the line. Nothing reads
|
|
311
|
+
* a changelog looking for it, so do not assume a guard notices when it is
|
|
312
|
+
* missing — none does.
|
|
306
313
|
*/
|
|
307
314
|
export const MIX_RESPONSE_MARKER = '**Governance mix:**';
|
|
308
315
|
|
|
309
316
|
/**
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
* A generator that emits the measured level alone would satisfy the gate's
|
|
313
|
-
* length floor (`MIX_RESPONSE_MIN_CHARS`, 40) with a number the machine
|
|
314
|
-
* produced, turning a written-answer obligation into a formality the tool
|
|
315
|
-
* discharges for itself. That is a smuggled auto-approval, not a fix.
|
|
317
|
+
* Render the measured governance-versus-product line the writer emits.
|
|
316
318
|
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* `DERIVED_MARKER` already follow. The net effect is only that the obligation
|
|
320
|
-
* moves from CI to the moment the section is written, where the fix is an edit
|
|
321
|
-
* to a file already open in the working tree.
|
|
322
|
-
*/
|
|
323
|
-
export const MIX_RESPONSE_PLACEHOLDER = '<the consumer work>';
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Every placeholder token the writer emits, so none of them can ship.
|
|
319
|
+
* ONE line, and machine-written end to end. It reports what the span measured
|
|
320
|
+
* and asks the releaser for nothing.
|
|
327
321
|
*
|
|
328
|
-
* The
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
322
|
+
* The written-answer obligation is gone (ADR-261). This used to emit a second line — `Next cycle ships <the consumer work>,
|
|
323
|
+
* tracked in <roadmap or issue>.` — a placeholder no generator was allowed to
|
|
324
|
+
* fill, backed by four refusals, an interactive prompt, a staging channel under
|
|
325
|
+
* `## [Unreleased]` and a read-back obligation on the following release. The
|
|
326
|
+
* releaser had to type prose into a running pipeline to get a version out.
|
|
332
327
|
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*/
|
|
338
|
-
export const MIX_RESPONSE_PLACEHOLDERS: readonly string[] = [
|
|
339
|
-
MIX_RESPONSE_PLACEHOLDER,
|
|
340
|
-
'<roadmap or issue>',
|
|
341
|
-
];
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Render the response block the writer emits when the obligation triggers.
|
|
328
|
+
* The owner ended it: this package exists to make the maintainer's work
|
|
329
|
+
* cheaper, and a release gate that stops a green pipeline until a human writes
|
|
330
|
+
* a sentence about a FUTURE cycle is the opposite. The measurement stays
|
|
331
|
+
* because it is free and true; the homework does not.
|
|
345
332
|
*
|
|
346
|
-
*
|
|
347
|
-
* `
|
|
348
|
-
*
|
|
349
|
-
*
|
|
333
|
+
* The removed placeholder constants (`MIX_RESPONSE_PLACEHOLDER(S)`) and promise
|
|
334
|
+
* sentinels (`PROMISE_PHRASE`, `PROMISE_READBACK_MARKER`, `PROMISE_OUTCOMES`)
|
|
335
|
+
* are deleted rather than deprecated — a sentinel nothing writes and nothing
|
|
336
|
+
* refuses is dead weight that reads as live policy.
|
|
350
337
|
*/
|
|
351
338
|
export function render_mix_response(level: string): string[] {
|
|
352
|
-
return [
|
|
353
|
-
`> ${MIX_RESPONSE_MARKER} ${level}.`,
|
|
354
|
-
`> ${PROMISE_PHRASE} ${MIX_RESPONSE_PLACEHOLDER}, tracked in <roadmap or issue>.`,
|
|
355
|
-
];
|
|
339
|
+
return [`> ${MIX_RESPONSE_MARKER} ${level}.`];
|
|
356
340
|
}
|
|
357
341
|
|
|
358
|
-
/**
|
|
359
|
-
* The words that turn a governance response into a PROMISE about a later cycle.
|
|
360
|
-
*
|
|
361
|
-
* Named rather than inlined because two sides now read it: the writer emits it,
|
|
362
|
-
* and the next release reads the previous section for it to decide whether a
|
|
363
|
-
* read-back is owed. A promise nothing reads back costs nothing to restate, so
|
|
364
|
-
* it can be restated for as many cycles as there are cycles — the defect this
|
|
365
|
-
* constant exists to make addressable.
|
|
366
|
-
*/
|
|
367
|
-
export const PROMISE_PHRASE = 'Next cycle ships';
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* The marker under which a release head answers the PREVIOUS head's promise.
|
|
371
|
-
*
|
|
372
|
-
* A named sentinel, on the same discipline as `MIX_RESPONSE_MARKER` and
|
|
373
|
-
* `DERIVED_MARKER`: a shape match over prose would accept a sentence that
|
|
374
|
-
* happens to mention last cycle and reject a reworded answer.
|
|
375
|
-
*/
|
|
376
|
-
export const PROMISE_READBACK_MARKER = '**Previous cycle:**';
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* The three outcomes a read-back may report, and the reason there are exactly
|
|
380
|
-
* three.
|
|
381
|
-
*
|
|
382
|
-
* `shipped` and `did not ship` are the honest halves of the fact. `withdrawn`
|
|
383
|
-
* exists because a promise the project decided not to keep is a legitimate
|
|
384
|
-
* third state — but only WITH a reason, which is why the answer also has to
|
|
385
|
-
* clear a written-length floor rather than being one of these three words on
|
|
386
|
-
* its own. Without the third state the mechanism would push an author toward
|
|
387
|
-
* restating a dead promise as still-outstanding, which is the failure it is
|
|
388
|
-
* built to stop.
|
|
389
|
-
*/
|
|
390
|
-
export const PROMISE_OUTCOMES: readonly string[] = ['shipped', 'did not ship', 'withdrawn'];
|
|
391
|
-
|
|
392
342
|
/** Every `## X.Y.Z` release version in the file, in document order (newest first). */
|
|
393
343
|
export function changelog_versions(text: string): string[] {
|
|
394
344
|
const out: string[] = [];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a release may START — the branch it may run from, and whether a dirty
|
|
3
|
+
* working tree is tolerated there.
|
|
4
|
+
*
|
|
5
|
+
* Its own module because `release.ts` sits 456 lines over the 1500-line source
|
|
6
|
+
* ratchet, so growing it is a gate failure by construction. `_lib/` is where
|
|
7
|
+
* `release_highlights.ts` and `release_material.ts` already put the predicates
|
|
8
|
+
* the pipeline and its gates share.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface PositionVerdict {
|
|
12
|
+
proceed: boolean;
|
|
13
|
+
/** Printed before the run continues. Never a refusal. */
|
|
14
|
+
notice?: string;
|
|
15
|
+
/** Printed on refusal (no trailing newline). */
|
|
16
|
+
message?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Where a release may START, resolved as a pure verdict (no I/O, so it is
|
|
21
|
+
* unit-testable — same shape as `confirmGate`).
|
|
22
|
+
*
|
|
23
|
+
* THE DEADLOCK THIS REMOVES.
|
|
24
|
+
*
|
|
25
|
+
* `guard_release_curation` refuses BETWEEN step 2 and step 3: step 1 has
|
|
26
|
+
* already created and checked out `release/{target}`, step 2 has already
|
|
27
|
+
* written the bump, the changelog section and every regenerated derived file,
|
|
28
|
+
* and nothing is committed yet. Its message says to curate the head and re-run
|
|
29
|
+
* `task release`. Measured on 14.19.0, neither re-run reached step 1:
|
|
30
|
+
*
|
|
31
|
+
* - `task release` → `release must run from 'main'` (this check)
|
|
32
|
+
* - `task release --resume` → `working tree is not clean` (this check)
|
|
33
|
+
*
|
|
34
|
+
* So the guard prescribed a remedy the preflight refused, in both spellings,
|
|
35
|
+
* and the only way through was to hand-craft the `release: X.Y.Z` commit the
|
|
36
|
+
* pipeline makes for itself one step later.
|
|
37
|
+
*
|
|
38
|
+
* `docs/release-runbook.md` already claimed this was closed on 2026-09-03 and
|
|
39
|
+
* the drill pins `plain-run-reuses-an-existing-branch` for it. Both were true
|
|
40
|
+
* of the fix that landed — in `checkout_release_branch`, which is STEP 1. The
|
|
41
|
+
* preflight runs before step 1, so from the release branch the run never
|
|
42
|
+
* arrived at the code that had been repaired. The claim was not wrong about
|
|
43
|
+
* step 1; it was wrong that step 1 was reachable from that position.
|
|
44
|
+
*
|
|
45
|
+
* Why `release/{target}` is a legitimate start, with or without `--resume`:
|
|
46
|
+
*
|
|
47
|
+
* It is the position the pipeline itself leaves behind. `--resume` remains
|
|
48
|
+
* meaningful for what it was always for — skipping a COMMIT, a PR, a tag or a
|
|
49
|
+
* Release that already exists — and the `!resume` tag-exists check in
|
|
50
|
+
* `preflight` still refuses a plain re-run of an already-tagged version.
|
|
51
|
+
*
|
|
52
|
+
* Why a dirty tree is accepted THERE and nowhere else:
|
|
53
|
+
*
|
|
54
|
+
* On `release/{target}` the dirt is this pipeline's own step-2 output, and
|
|
55
|
+
* step 3 sweeps it with `git add -A` regardless. Refusing it can therefore
|
|
56
|
+
* only ever fire against the tool's own state. On `main` the same tree is an
|
|
57
|
+
* operator's unrelated work about to be swept into a release commit, so the
|
|
58
|
+
* refusal stays exactly as it was. The accepted files are PRINTED rather than
|
|
59
|
+
* absorbed silently, and `check_release_pr_shape` still refuses anything
|
|
60
|
+
* outside the version-bump allowlist on the resulting PR — two nets under the
|
|
61
|
+
* one place this relaxes.
|
|
62
|
+
*/
|
|
63
|
+
export function preflightPosition(opts: {
|
|
64
|
+
branch: string;
|
|
65
|
+
mainBranch: string;
|
|
66
|
+
releaseBranch: string;
|
|
67
|
+
porcelain: string;
|
|
68
|
+
}): PositionVerdict {
|
|
69
|
+
const { branch, mainBranch, releaseBranch, porcelain } = opts;
|
|
70
|
+
if (branch !== mainBranch && branch !== releaseBranch) {
|
|
71
|
+
return {
|
|
72
|
+
proceed: false,
|
|
73
|
+
message:
|
|
74
|
+
`release must run from '${mainBranch}' or '${releaseBranch}', ` +
|
|
75
|
+
`currently on '${branch}'`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (!porcelain.trim()) {
|
|
79
|
+
return { proceed: true };
|
|
80
|
+
}
|
|
81
|
+
if (branch === mainBranch) {
|
|
82
|
+
return { proceed: false, message: 'working tree is not clean; commit or stash first' };
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
proceed: true,
|
|
86
|
+
notice:
|
|
87
|
+
`working tree is not clean, and on '${releaseBranch}' that is this pipeline's own ` +
|
|
88
|
+
'step-2 output — step 3 commits it with `git add -A`. Files that will be swept ' +
|
|
89
|
+
'into the release commit:\n' +
|
|
90
|
+
porcelain
|
|
91
|
+
.split('\n')
|
|
92
|
+
.filter((l) => l.trim())
|
|
93
|
+
.map((l) => ` ${l}`)
|
|
94
|
+
.join('\n'),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
*/
|
|
35
35
|
export type MutationMeasure = 'exact' | 'capped_approximation';
|
|
36
36
|
|
|
37
|
+
import { appliedIds } from './audit_field_provenance.js';
|
|
37
38
|
import type { NoFreeForm } from './runtime_journal.js';
|
|
38
39
|
import { type PrivacyClass } from './privacy_class.js';
|
|
39
40
|
|
|
@@ -55,6 +56,20 @@ export interface ReviewSkippedInput {
|
|
|
55
56
|
/** Stable id (ULID, UUID, or content hash); caller supplies. */
|
|
56
57
|
id: string;
|
|
57
58
|
work_id?: string | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Stable rule ids the run ACTUALLY carried. Absent means `[]`, an honest
|
|
61
|
+
* absence — this writer observes a review that did NOT happen, so it
|
|
62
|
+
* usually has no rules observation to offer in either direction.
|
|
63
|
+
*
|
|
64
|
+
* It wrote the literal `['delegation-policy']` on every line until
|
|
65
|
+
* 2026-09-07, which made the whole audit stream's most frequent "pattern" a
|
|
66
|
+
* property of the writer. See `_lib/audit_field_provenance.ts` for the
|
|
67
|
+
* measurement, and note the difference from `skills_applied` below: that
|
|
68
|
+
* field is OMITTED because audit-log-v1 lets absence mean "not recorded",
|
|
69
|
+
* while `rules_applied` is mandatory on every line and `[]` is how absence
|
|
70
|
+
* is spelled there.
|
|
71
|
+
*/
|
|
72
|
+
rules_applied?: string[] | null | undefined;
|
|
58
73
|
}
|
|
59
74
|
|
|
60
75
|
export interface BuiltReviewSkippedLine {
|
|
@@ -102,7 +117,8 @@ export function buildReviewSkippedLine(input: ReviewSkippedInput): BuiltReviewSk
|
|
|
102
117
|
risk_class: input.diff_lines >= HIGH_RISK_DIFF_LINES ? 'high' : 'medium',
|
|
103
118
|
memory: { asks: 0, hits: 0 },
|
|
104
119
|
verify: { claims: 0, first_try_passes: 0 },
|
|
105
|
-
|
|
120
|
+
// COMPUTED, never a producer constant.
|
|
121
|
+
rules_applied: appliedIds(input.rules_applied),
|
|
106
122
|
// MANDATORY on every audit line, same as the orchestration producer.
|
|
107
123
|
privacy_class: PRODUCER_PRIVACY_CLASS,
|
|
108
124
|
// `skills_applied` is OMITTED here, deliberately and not by oversight.
|