@event4u/agent-config 9.24.0 → 9.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +136 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/conformance.md +182 -0
- package/dist/agent-src/commands/analyze/inbox.md +33 -2
- package/dist/agent-src/commands/review/changes.md +6 -5
- package/dist/agent-src/commands/roadmap/next.md +194 -0
- package/dist/agent-src/commands/roadmap.md +12 -4
- package/dist/agent-src/contexts/execution/subagent-routing.md +4 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +2 -0
- package/dist/agent-src/rules/delegation-policy.md +3 -2
- package/dist/agent-src/rules/design-fidelity.md +17 -1
- package/dist/agent-src/rules/design-review-after-ui-write.md +116 -0
- package/dist/agent-src/rules/evaluator-independence.md +111 -0
- package/dist/agent-src/rules/session-canary.md +15 -1
- package/dist/agent-src/rules/settings-ask-protocol.md +176 -0
- package/dist/agent-src/rules/token-budget-discipline.md +6 -3
- package/dist/agent-src/rules/ui-audit-gate.md +3 -0
- package/dist/agent-src/rules/user-interaction.md +23 -0
- package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +1 -1
- package/dist/agent-src/skills/recursive-verification/SKILL.md +13 -0
- package/dist/agent-src/skills/rule-writing/SKILL.md +45 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +114 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/cmd_route_audit.js +4 -1
- package/dist/cli-delegate/cmd_route_explain.js +4 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +146 -28
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +5 -5
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +16 -11
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +12 -7
- package/dist/hooks/dispatch.js +1501 -511
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/router.json +1 -1
- package/dist/server/nicknameResolver.js +116 -0
- package/dist/server/nicknameResolver.js.map +1 -0
- package/dist/server/routes/ping.js +29 -11
- package/dist/server/routes/ping.js.map +1 -1
- package/dist/shared/interactiveContext.js +73 -0
- package/dist/shared/interactiveContext.js.map +1 -0
- package/dist/shared/nicknamePrefill.js +59 -0
- package/dist/shared/nicknamePrefill.js.map +1 -0
- package/dist/shared/settingsAsks.js +128 -0
- package/dist/shared/settingsAsks.js.map +1 -0
- package/dist/shared/settingsConsent.js +93 -0
- package/dist/shared/settingsConsent.js.map +1 -0
- package/package.json +1 -1
- package/src/config/agents-paths.yml +15 -0
- package/src/config/evaluator-budgets.json +68 -68
- package/src/config/gate-coverage.yml +131 -5
- package/src/config/gate-violation-baselines.json +11 -0
- package/src/scripts/_cli/cmd_route_audit.ts +4 -1
- package/src/scripts/_cli/cmd_route_explain.ts +9 -1
- package/src/scripts/_dispatch.bash +12 -0
- package/src/scripts/ai-video/stitch.sh +27 -9
- package/src/scripts/before_complete_hook.ts +146 -5
- package/src/scripts/bench_ab_v2_run.ts +272 -16
- package/src/scripts/bench_ab_v2_stats.ts +156 -3
- package/src/scripts/check_completion_review.ts +80 -3
- package/src/scripts/check_corpus_staleness.ts +692 -0
- package/src/scripts/check_depth_budget.ts +295 -0
- package/src/scripts/check_gate_coverage.ts +67 -0
- package/src/scripts/check_hook_bundle_freshness.ts +183 -0
- package/src/scripts/conformance_scan.ts +422 -0
- package/src/scripts/dispatch_r2_reviewer.ts +31 -2
- package/src/scripts/git_authorization_hook.ts +328 -0
- package/src/scripts/hook_manifest.yaml +69 -9
- package/src/scripts/hooks/block_no_verify.ts +3 -1
- package/src/scripts/hooks/block_unauthorized_git.ts +371 -0
- package/src/scripts/hooks/concern_registry.ts +8 -0
- package/src/scripts/hooks/design_slop_hook.ts +13 -1
- package/src/scripts/hooks/dispatch_hook.ts +97 -2
- package/src/scripts/hooks/evidence_independence.ts +368 -0
- package/src/scripts/language_mirror_hook.ts +456 -0
- package/src/scripts/lint_abstraction_thresholds.ts +41 -8
- package/src/scripts/lint_agents_layout.ts +2 -0
- package/src/scripts/lint_decision_impact.ts +246 -0
- package/src/scripts/lint_example_fences.ts +609 -0
- package/src/scripts/lint_hook_manifest.ts +29 -0
- package/src/scripts/lint_rule_interactions.ts +90 -0
- package/src/scripts/lint_rule_migration_ledger.ts +480 -0
- package/src/scripts/lint_skill_descriptions.ts +86 -2
- package/src/scripts/memory_learn_hook.ts +109 -6
- package/src/scripts/pr_url_reminder_hook.ts +17 -2
- package/src/scripts/release.ts +38 -1
- package/src/scripts/schemas/review-findings.schema.json +99 -0
- package/src/scripts/schemas/rule.schema.json +10 -0
- package/src/shared/interactiveContext.ts +98 -0
- package/src/shared/nicknamePrefill.ts +96 -0
- package/src/shared/settingsAsks.ts +176 -0
- package/src/shared/settingsConsent.ts +136 -0
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
#
|
|
43
43
|
# COVERAGE — THE HONEST DENOMINATOR (re-measured 2026-08-05)
|
|
44
44
|
# ----------------------------------------------------------
|
|
45
|
-
#
|
|
45
|
+
# 240 `lint_*` / `check_*` / `audit_*` scripts live under `src/scripts/`
|
|
46
46
|
# (the count is computed, not asserted — `check_gate_coverage.count_gate_scripts`).
|
|
47
|
-
# **
|
|
48
|
-
# CI-identical argv, and all
|
|
49
|
-
# complete with respect to its own contract, and covers ~
|
|
47
|
+
# **32 of them emit the machine-readable `scanned: <N>` line under their
|
|
48
|
+
# CI-identical argv, and all 31 are listed below.** The manifest is therefore
|
|
49
|
+
# complete with respect to its own contract, and covers ~13 % of the population.
|
|
50
50
|
#
|
|
51
51
|
# Two revisions of this header have now gone stale the same way. It claimed
|
|
52
52
|
# "8 of 211", then "22 of 223" while 26 entries were listed. Both halves drift
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
# stays a manual count until something computes it too. If you add an entry,
|
|
58
58
|
# the two numbers in the paragraph above are part of the diff.
|
|
59
59
|
#
|
|
60
|
-
# The other ~
|
|
60
|
+
# The other ~209 are NOT covered, for one reason: rule 1. They print their count
|
|
61
61
|
# in prose ("111 file(s) scanned", "0 declarer(s)") or not at all, and a guard
|
|
62
62
|
# that parses prose is the fragile thing being replaced. Padding this list with
|
|
63
63
|
# them would manufacture exactly the false green the file exists to prevent.
|
|
@@ -501,3 +501,129 @@ gates:
|
|
|
501
501
|
create-only by design. Discrimination is proven instead by the gate's own
|
|
502
502
|
`--self-test`, which drives the real binary through six rejecting cases
|
|
503
503
|
including the dead-scan-root one.
|
|
504
|
+
|
|
505
|
+
- id: lint_example_fences
|
|
506
|
+
argv: ["--strict"]
|
|
507
|
+
min_scanned: 450
|
|
508
|
+
corpus: "authored markdown whose fenced blocks are code the reader copies — src/rules/*.md, src/skills/*/SKILL.md, docs/guidelines/**/*.md (503 at baseline)"
|
|
509
|
+
status: enforced
|
|
510
|
+
canary:
|
|
511
|
+
class: undetected-antipattern
|
|
512
|
+
path: docs/guidelines/zz-canary-example-fence.md
|
|
513
|
+
content: |
|
|
514
|
+
# Canary
|
|
515
|
+
|
|
516
|
+
Planted canary: a fenced example teaching the render sink the
|
|
517
|
+
`frontend-render-security` rule forbids three paragraphs away.
|
|
518
|
+
|
|
519
|
+
```js
|
|
520
|
+
el.innerHTML = untrustedUserBio;
|
|
521
|
+
```
|
|
522
|
+
note: >
|
|
523
|
+
Registered STRICT on day one, which is the opposite of the advisory
|
|
524
|
+
landing its roadmap step planned for — because the classification that
|
|
525
|
+
step demands came back **zero hits over all 503 files**, and an advisory
|
|
526
|
+
gate over an empty violation set is a gate that can only ever be ignored.
|
|
527
|
+
The zero is verified rather than assumed: every raw occurrence of the
|
|
528
|
+
eight registry tokens in the tree is prose forbidding the pattern, a table
|
|
529
|
+
row, or an `rg` search string inside a shell fence, and a liveness probe
|
|
530
|
+
that injected a violation INTO a real corpus file fired while the
|
|
531
|
+
untouched original stayed clean. Floor at 450 against a live 503 leaves
|
|
532
|
+
room for skill retirement without redding the change that retires one.
|
|
533
|
+
|
|
534
|
+
- id: check_depth_budget
|
|
535
|
+
argv: []
|
|
536
|
+
min_scanned: 140
|
|
537
|
+
corpus: "on-demand depth files — src/agent-src/contexts/**/*.md + docs/guidelines/**/*.md (161 at baseline)"
|
|
538
|
+
status: enforced
|
|
539
|
+
canary:
|
|
540
|
+
class: budget-blind
|
|
541
|
+
path: docs/guidelines/zz-canary-depth-budget.md
|
|
542
|
+
content: |
|
|
543
|
+
# Canary
|
|
544
|
+
|
|
545
|
+
Planted canary: a depth file over the 16,000-char ceiling, which must
|
|
546
|
+
make the shrink-only ratchet red as the fifth violation.
|
|
547
|
+
|
|
548
|
+
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
549
|
+
note: >
|
|
550
|
+
The ratchet baseline is 4, so this gate is green on the current tree and
|
|
551
|
+
reds on a FIFTH over-ceiling depth file. Both halves are proven rather
|
|
552
|
+
than assumed: planting a 16,100-char file took it to exit 1 and removing
|
|
553
|
+
the file took it back to exit 0. Floor at 140 against a live 161 leaves
|
|
554
|
+
room for consolidating reference documents — which is the direction this
|
|
555
|
+
ratchet is meant to push — without redding the change that consolidates.
|
|
556
|
+
|
|
557
|
+
- id: lint_rule_migration_ledger
|
|
558
|
+
argv: ["--quiet"]
|
|
559
|
+
min_scanned: 35
|
|
560
|
+
corpus: "rules whose body was migrated out to a skill, guideline, context or contract (44 at baseline)"
|
|
561
|
+
status: enforced
|
|
562
|
+
canary:
|
|
563
|
+
class: unledgered-migration
|
|
564
|
+
path: src/rules/zz-canary-migrated-rule.md
|
|
565
|
+
content: |
|
|
566
|
+
---
|
|
567
|
+
type: auto
|
|
568
|
+
description: "Planted canary rule whose body was migrated out with no ledger behind it."
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
# Canary
|
|
572
|
+
|
|
573
|
+
Body migrated to `skill:docker`.
|
|
574
|
+
note: >
|
|
575
|
+
The canary plants a rule carrying a migration pointer and no ledger, which
|
|
576
|
+
is the defect the gate exists for — a lossy transform with no record.
|
|
577
|
+
Floor at 35 against a live 44 leaves room for a rule un-migrating (its
|
|
578
|
+
body coming back) without redding the change that does it.
|
|
579
|
+
|
|
580
|
+
- id: lint_decision_impact
|
|
581
|
+
argv: ["--quiet"]
|
|
582
|
+
min_scanned: 90
|
|
583
|
+
corpus: "src/rules/*.md frontmatters (111 at baseline)"
|
|
584
|
+
status: enforced
|
|
585
|
+
canary:
|
|
586
|
+
class: unevidenced-removal-class
|
|
587
|
+
path: src/rules/zz-canary-decision-impact.md
|
|
588
|
+
content: |
|
|
589
|
+
---
|
|
590
|
+
type: auto
|
|
591
|
+
description: "Planted canary claiming the removal-permitting class with no evidence behind it."
|
|
592
|
+
decision_impact: already-complied-with
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
# Canary
|
|
596
|
+
note: >
|
|
597
|
+
Verified-empty by design: 111 rules read, 0 classified, because the field
|
|
598
|
+
is optional and backfill is opportunistic (a 111-rule batch trips
|
|
599
|
+
check_kernel_prefix_stability). The canary is what distinguishes that from
|
|
600
|
+
a gate scanning nothing — it plants the removal-permitting class with no
|
|
601
|
+
evidence and the gate reds.
|
|
602
|
+
|
|
603
|
+
- id: check_corpus_staleness
|
|
604
|
+
argv: ["--quiet", "--today", "2026-08-06"]
|
|
605
|
+
min_scanned: 5
|
|
606
|
+
corpus: "src/skills/*/data/manifest.json — grounding-corpus manifests (6 at baseline; brand, database, accessibility-auditor, api-design, design-intelligence, threat-modeling)"
|
|
607
|
+
status: enforced
|
|
608
|
+
canary:
|
|
609
|
+
class: missing-declared-csv
|
|
610
|
+
path: src/skills/zz-canary-corpus/data/manifest.json
|
|
611
|
+
content: |
|
|
612
|
+
{
|
|
613
|
+
"manifest_version": 1,
|
|
614
|
+
"refresh_cadence": "quarterly",
|
|
615
|
+
"upstream": { "last_checked": "2026-07-01" },
|
|
616
|
+
"domains": { "d": { "file": "nope.csv", "search_cols": ["a"], "output_cols": ["b"] } }
|
|
617
|
+
}
|
|
618
|
+
note: >
|
|
619
|
+
`--today` is PINNED in argv on purpose, and it is the whole reason this
|
|
620
|
+
gate is testable at all: the five upstream-vendored corpora share
|
|
621
|
+
`last_checked: 2026-06-07`, so an unpinned reference date makes this row
|
|
622
|
+
flip from green to red on 2026-09-15 with no diff behind it. The pinned
|
|
623
|
+
date keeps the coverage guard measuring COVERAGE; the real calendar check
|
|
624
|
+
is the workflow step, which runs unpinned and is meant to red when the
|
|
625
|
+
cadence is actually missed. Changing the pin is how you re-baseline after a
|
|
626
|
+
re-vendor, and it should be visible in a diff.
|
|
627
|
+
The canary plants a manifest whose declared CSV does not exist — the exact
|
|
628
|
+
gap `corpus-grounding/scripts/schema_validator.ts` leaves open, since it
|
|
629
|
+
computes the path and never opens the file.
|
|
@@ -26,6 +26,17 @@
|
|
|
26
26
|
"landed": "2026-08-05",
|
|
27
27
|
"note": "Ledger adoption ratchet. 217 of 221 CI-registered gates do not yet carry per-target completeness accounting (`_lib/gate_ledger.ts`) or a `// ledger-exempt:` marker. This is deliberately NOT a sweep: road-to-skill-ecosystem-gate-integrity Phase 1 adopts exactly three gates (lint_framework_leakage, check_references, lint_roadmap_complexity) plus the checker itself to prove the interface, and the remainder migrates as gates are touched. Shrink-only from here — the number may not grow, so a NEW gate must adopt or exempt.",
|
|
28
28
|
"falsifier": "./scripts-run src/scripts/check_gate_completeness"
|
|
29
|
+
},
|
|
30
|
+
"check_depth_budget": {
|
|
31
|
+
"count": 4,
|
|
32
|
+
"landed": "2026-08-06",
|
|
33
|
+
"note": "Four depth files exceed the 16,000-char per-file ceiling: rule-body-migration-inventory.md (23,782), design-antipatterns.md (20,707), roadmap-process-loop.md (20,360), orchestration-telemetry.md (16,155). Recorded rather than fixed because splitting four large reference documents is unrelated to the change that introduced the ceiling, and a gate that lands as four instant blockers is the shape this repository has already recorded as a failure. Shrink-only from here: a FIFTH over-ceiling file reds. roadmap-process-loop.md is the file that motivated the gate — it states 'Size budget: <= 4,000 chars' in its own header and is 5x over it, which is what a declared budget with nothing measuring it decays into. The ceiling is a GROWTH RATCHET, not a measured quality threshold: the 7,308-trajectory study behind the 3,500-token skill ceiling (ADR-217) was taken on procedural always-co-loaded skills, and this layer is declarative and loaded one or two files at a time. Replace the number when someone measures comprehension on THIS layer."
|
|
34
|
+
},
|
|
35
|
+
"gate-self-test:registered-non-adopters": {
|
|
36
|
+
"count": 24,
|
|
37
|
+
"landed": "2026-08-06",
|
|
38
|
+
"note": "Self-test adoption ratchet, expressed as a shrink-only NON-adopter count. 24 of the 32 gates registered enforced in gate-coverage.yml carry neither a `--self-test` (`_lib/gate_self_test.ts`) nor a `// self-test-exempt: <reason>` marker, so nothing proves they DISCRIMINATE — an enforced `scanned:` floor proves a gate read something, only a self-test proves the reading changes the verdict. Deliberately NOT an adoption column ratcheted upward: check_gate_coverage.ts's own report_hardening_ratchet rejects that shape in this same file (\"grades the solution instead of the problem\"), and the sibling adoption ratchet (check_gate_completeness) is already a non-adopter count. Population is the manifest, not every script under src/scripts: padding the manifest to move this number would mean adding a floor per row. Shrink-only from here — a NEW registered gate must adopt or exempt. Migration mechanism, same as the ledger ratchet above: gates adopt as they are touched, not in a sweep — the eight current adopters were each added alongside a real change to that gate. The 56-day non-stagnation clause applies unchanged, so a count parked here fails on its own.",
|
|
39
|
+
"falsifier": "./scripts-run src/scripts/check_gate_coverage"
|
|
29
40
|
}
|
|
30
41
|
}
|
|
31
42
|
}
|
|
@@ -36,8 +36,11 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
36
36
|
import { match_prompt, type Router } from '../_lib/router_match.js';
|
|
37
37
|
import { load_agent_settings } from '../_lib/agent_settings.js';
|
|
38
38
|
import { read_entries } from '../chat_history.js';
|
|
39
|
+
import { resolvePackageRoot } from '../_lib/package_root.js';
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
// Marker-anchored, not hop-counted — see the sibling note in cmd_route_explain.ts.
|
|
42
|
+
// Both routing commands were the last two `_cli` entries still counting hops.
|
|
43
|
+
const REPO_ROOT = resolvePackageRoot(import.meta.url);
|
|
41
44
|
const ROUTER_JSON = path.join(REPO_ROOT, 'dist', 'router.json');
|
|
42
45
|
|
|
43
46
|
// cache-version: v1 — rebuildable audit log: delete-and-rerun rebuilds it from
|
|
@@ -31,8 +31,16 @@ import {
|
|
|
31
31
|
} from '../_lib/router_match.js';
|
|
32
32
|
import { measure, method_note } from '../_lib/token_count.js';
|
|
33
33
|
import { load_agent_settings } from '../_lib/agent_settings.js';
|
|
34
|
+
import { resolvePackageRoot } from '../_lib/package_root.js';
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
// Marker-anchored, not hop-counted: this module runs from `src/scripts/_cli/`
|
|
37
|
+
// (three levels down) AND from the precompiled `dist/cli-delegate/` (two), and
|
|
38
|
+
// a fixed hop count is right in exactly one of them. Three hops from the bundle
|
|
39
|
+
// landed on `<pkg>/..` — `node_modules/@event4u` for a scoped install — so
|
|
40
|
+
// `route:explain` and `route:audit` reported dist/router.json as missing on
|
|
41
|
+
// every invocation through the shipped binary while both worked under `tsx`.
|
|
42
|
+
// The resolver exists precisely for this and 12 sibling commands already use it.
|
|
43
|
+
const REPO_ROOT = resolvePackageRoot(import.meta.url);
|
|
36
44
|
const ROUTER_JSON = path.join(REPO_ROOT, 'dist', 'router.json');
|
|
37
45
|
const RULES_DIST_DIR = path.join(REPO_ROOT, 'dist', 'agent-src', 'rules');
|
|
38
46
|
|
|
@@ -124,6 +124,7 @@ Tier 1 — power-user (release shape, audit, migration):
|
|
|
124
124
|
user-authored files; persists standing consent
|
|
125
125
|
(install.auto_converge) on first use.
|
|
126
126
|
Flags: --dry-run | --yes
|
|
127
|
+
conformance:behavior Replay local transcripts through the mechanised conformance checks
|
|
127
128
|
conformance Consumer conformance contract: doctor --ci +
|
|
128
129
|
installed-and-firing checks. Exits non-zero on
|
|
129
130
|
a failed check.
|
|
@@ -1219,6 +1220,16 @@ cmd_analyze_session() {
|
|
|
1219
1220
|
exec_ts "$PACKAGE_ROOT/src/scripts/_cli/cmd_analyze_session.ts" "$@"
|
|
1220
1221
|
}
|
|
1221
1222
|
|
|
1223
|
+
# `agent-config conformance:behavior` — replay the local transcript store
|
|
1224
|
+
# through the four MECHANISED conformance checks (language pin, git
|
|
1225
|
+
# authorization, vacuous evidence, evidence steering). Report, not gate:
|
|
1226
|
+
# exit 0 unless the store is unreadable. See src/scripts/conformance_scan.ts.
|
|
1227
|
+
cmd_conformance_behavior() {
|
|
1228
|
+
local script
|
|
1229
|
+
script="$(resolve_script "src/scripts/conformance_scan.ts" "dist/agent-src/scripts/conformance_scan.ts")"
|
|
1230
|
+
exec_ts "$script" "$@"
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1222
1233
|
# `agent-config handoff` — pick a recent session, generate a deterministic
|
|
1223
1234
|
# handoff into agents/runtime/state/handoff-context.md, optionally --launch a
|
|
1224
1235
|
# fresh host session. See scripts/_cli/cmd_handoff.ts.
|
|
@@ -1240,6 +1251,7 @@ main() {
|
|
|
1240
1251
|
graph-explain) cmd_graph_explain "$@" ;;
|
|
1241
1252
|
benchmark) cmd_benchmark "$@" ;;
|
|
1242
1253
|
code-graph) cmd_code_graph "$@" ;;
|
|
1254
|
+
conformance:behavior) cmd_conformance_behavior "$@" ;;
|
|
1243
1255
|
roadmap:progress) cmd_roadmap_progress "$@" ;;
|
|
1244
1256
|
roadmap:progress-check) cmd_roadmap_progress_check "$@" ;;
|
|
1245
1257
|
roadmap:archive) cmd_roadmap_archive "$@" ;;
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
# stitch.sh <manifest.json> <output.mp4>
|
|
11
11
|
# [--skip-scene <id> ...]
|
|
12
12
|
# [--abort-on-missing | --continue]
|
|
13
|
-
#
|
|
13
|
+
#
|
|
14
|
+
# NOT a flag: --crossfade. It is parsed and REFUSED (exit 2). See
|
|
15
|
+
# "Refused flags" below — it is listed so a caller who read the old
|
|
16
|
+
# usage line gets an error instead of a hard cut.
|
|
14
17
|
#
|
|
15
18
|
# Failure semantics:
|
|
16
19
|
# - Missing clip → fail loud with scene_id and re-render hint
|
|
@@ -21,6 +24,22 @@
|
|
|
21
24
|
# <project>/scenes/<id>/error.json; stitch.sh checks for that
|
|
22
25
|
# file alongside the clip and surfaces it instead of an opaque
|
|
23
26
|
# ffmpeg error.
|
|
27
|
+
#
|
|
28
|
+
# Refused flags:
|
|
29
|
+
# --crossfade <seconds> — accepted by the parser, then REFUSED with
|
|
30
|
+
# exit 2 before any work. It used to print "not yet implemented"
|
|
31
|
+
# to stderr and fall straight through to the plain concat, so the
|
|
32
|
+
# script reported success (exit 0, success JSON) while delivering a
|
|
33
|
+
# hard cut. Worse, the notice sat AFTER the dry-run exit, and
|
|
34
|
+
# AIV_DRYRUN defaults to true — so in the default mode the flag was
|
|
35
|
+
# accepted in total silence.
|
|
36
|
+
# A crossfade is not a small addition here: the output path is
|
|
37
|
+
# `-f concat -c copy`, and stream copy precludes any filtergraph, so
|
|
38
|
+
# xfade/acrossfade need a re-encode path that does not exist. It was
|
|
39
|
+
# also never reachable from a governed surface: /video:stitch passes
|
|
40
|
+
# only --skip-scene and --continue and rejects anything else. So the
|
|
41
|
+
# honest state is a refusal, not a silent downgrade and not a
|
|
42
|
+
# filtergraph shipped into a script with no test harness.
|
|
24
43
|
|
|
25
44
|
set -euo pipefail
|
|
26
45
|
|
|
@@ -39,19 +58,23 @@ esac
|
|
|
39
58
|
MANIFEST="${1:-}"
|
|
40
59
|
OUTPUT="${2:-}"
|
|
41
60
|
[ -n "${MANIFEST}" ] && [ -n "${OUTPUT}" ] \
|
|
42
|
-
|| aiv_die 2 "usage: stitch.sh <manifest.json> <output.mp4> [--skip-scene <id>] [--abort-on-missing|--continue]
|
|
61
|
+
|| aiv_die 2 "usage: stitch.sh <manifest.json> <output.mp4> [--skip-scene <id>] [--abort-on-missing|--continue]"
|
|
43
62
|
[ -r "${MANIFEST}" ] || aiv_die 2 "manifest not readable: ${MANIFEST}"
|
|
44
63
|
shift 2 || true
|
|
45
64
|
|
|
46
65
|
SKIP_IDS=""
|
|
47
66
|
MISSING_POLICY="abort"
|
|
48
|
-
CROSSFADE=""
|
|
49
67
|
while [ "$#" -gt 0 ]; do
|
|
50
68
|
case "$1" in
|
|
51
69
|
--skip-scene) SKIP_IDS="${SKIP_IDS} ${2:-}"; shift 2 ;;
|
|
52
70
|
--abort-on-missing) MISSING_POLICY="abort"; shift ;;
|
|
53
71
|
--continue) MISSING_POLICY="continue"; shift ;;
|
|
54
|
-
--crossfade)
|
|
72
|
+
--crossfade)
|
|
73
|
+
# Refused HERE — before the dry-run branch, before ffmpeg, before any
|
|
74
|
+
# output. The old code warned after the dry-run exit and then concatenated
|
|
75
|
+
# anyway, so a caller asking for a crossfade got a hard cut and exit 0.
|
|
76
|
+
aiv_die 2 "stitch.sh: --crossfade is not implemented and will not be silently downgraded. The output path is 'ffmpeg -f concat -c copy', and stream copy precludes any filtergraph, so xfade/acrossfade require a re-encode path this script does not have. Re-run without --crossfade for a hard-cut concat, or open a roadmap item for the re-encode path."
|
|
77
|
+
;;
|
|
55
78
|
*) aiv_die 2 "stitch.sh: unknown flag '$1'" ;;
|
|
56
79
|
esac
|
|
57
80
|
done
|
|
@@ -149,11 +172,6 @@ if [ "${DRYRUN_FLAG}" -eq 1 ]; then
|
|
|
149
172
|
exit 0
|
|
150
173
|
fi
|
|
151
174
|
|
|
152
|
-
if [ -n "${CROSSFADE}" ]; then
|
|
153
|
-
printf 'stitch: crossfade=%ss requested — passing through ffmpeg xfade filter not yet implemented (concat path used)\n' \
|
|
154
|
-
"${CROSSFADE}" >&2
|
|
155
|
-
fi
|
|
156
|
-
|
|
157
175
|
ffmpeg -loglevel error -y -f concat -safe 0 -i "${CONCAT_LIST}" \
|
|
158
176
|
-c copy "${OUTPUT}" >/dev/null \
|
|
159
177
|
|| aiv_die 8 "ffmpeg concat failed (manifest=${MANIFEST}, output=${OUTPUT})"
|
|
@@ -111,6 +111,8 @@ function _empty_state(): StateDict {
|
|
|
111
111
|
verifications_this_session: 0,
|
|
112
112
|
last_stop_at: null,
|
|
113
113
|
verified_this_turn: false,
|
|
114
|
+
ci_saw_pending: false,
|
|
115
|
+
nonevidence_this_turn: 0,
|
|
114
116
|
checked_at: _now(),
|
|
115
117
|
};
|
|
116
118
|
}
|
|
@@ -163,7 +165,113 @@ function _extract_command(payload: StateDict): [string | null, string | null] {
|
|
|
163
165
|
}
|
|
164
166
|
|
|
165
167
|
function _is_verification(command: string): boolean {
|
|
166
|
-
|
|
168
|
+
// CI polls join the verification set here (they did not match the local
|
|
169
|
+
// runner pattern). A settled green CI run IS evidence — but only once the
|
|
170
|
+
// settle is genuine, which is what the FC-3b guard below decides. Before this
|
|
171
|
+
// change a CI poll counted for nothing at all, so nothing observed the
|
|
172
|
+
// difference between "settled" and "never started".
|
|
173
|
+
return _VERIFICATION_RE.test(command) || isCiPoll(command);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Non-vacuity guard (conformance audit 2026-08-06, failure class FC-3b).
|
|
178
|
+
*
|
|
179
|
+
* The measured failure: a CI poll landing in the gap between `git push` and
|
|
180
|
+
* GitHub registering the checks returned `0 pass / 0 fail`, so the agent's exit
|
|
181
|
+
* condition `pending == 0` was TRIVIALLY satisfied and "CI settled" was reported
|
|
182
|
+
* twice — on a run that had not started. The verification *command* ran, so the
|
|
183
|
+
* evidence gate felt satisfied. Nothing checked that its output said anything.
|
|
184
|
+
*
|
|
185
|
+
* A result set of size zero is not evidence. `∀x ∈ ∅` is vacuously true, and
|
|
186
|
+
* that is a logic error rather than a judgement call, which is why it is gated
|
|
187
|
+
* here rather than described in a rule.
|
|
188
|
+
*/
|
|
189
|
+
const _VACUOUS_PATTERNS: readonly RegExp[] = [
|
|
190
|
+
// gh pr checks / gh run — no checks registered at all.
|
|
191
|
+
/\b0\s+(?:checks?|runs?)\b/i,
|
|
192
|
+
// Test runners reporting an empty run.
|
|
193
|
+
/\bno tests? (?:ran|found|to run|were run)\b/i,
|
|
194
|
+
/\bNo test files found\b/i,
|
|
195
|
+
/\b0\s+pass(?:ed|ing)?\b[\s,|·]*\b0\s+fail(?:ed|ing|ures)?\b/i,
|
|
196
|
+
/\bTests?\s+0\s+passed\b/i,
|
|
197
|
+
/\bRan 0 tests?\b/i,
|
|
198
|
+
// Linters / scanners with an empty target set.
|
|
199
|
+
/\bno files? (?:matched|to (?:lint|check|scan)|found)\b/i,
|
|
200
|
+
/\b0\s+files?\s+(?:checked|scanned|linted)\b/i,
|
|
201
|
+
/\bnothing to check\b/i,
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
/** True when a verification command's output proves nothing because it covered nothing. */
|
|
205
|
+
export function isVacuousOutput(output: string): boolean {
|
|
206
|
+
const text = output.trim();
|
|
207
|
+
if (!text) {
|
|
208
|
+
// Silence is the Unix convention for success: a clean `tsc --noEmit`,
|
|
209
|
+
// `eslint`, or `phpstan` prints nothing. Round 2 caught this reading empty
|
|
210
|
+
// output as vacuity, which quietly stopped counting the most common green
|
|
211
|
+
// signal in this repo. Genuinely unreadable CI polls are handled separately
|
|
212
|
+
// (`pendingCount` returns null and the poll counts for nothing).
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
// Per RESULT LINE, not per blob: a composite run whose 812 tests passed and
|
|
216
|
+
// whose one empty sub-package printed "No test files found" is evidence.
|
|
217
|
+
const lines = text.split("\n").filter((l) => l.trim());
|
|
218
|
+
return lines.every((l) => _VACUOUS_PATTERNS.some((re) => re.test(l)));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Commands that poll CI state rather than produce a local result. */
|
|
222
|
+
const _CI_POLL_RE = /\bgh\s+(?:pr\s+checks|run\s+(?:watch|list|view))\b/i;
|
|
223
|
+
|
|
224
|
+
export function isCiPoll(command: string): boolean {
|
|
225
|
+
return _CI_POLL_RE.test(command);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Read a pending-check count out of a CI poll's output. Returns null when the
|
|
230
|
+
* shape is unrecognised — an unknown shape must not be read as "settled".
|
|
231
|
+
*/
|
|
232
|
+
export function pendingCount(output: string): number | null {
|
|
233
|
+
const m = /(\d+)\s+(?:checks? )?pending\b/i.exec(output) ?? /\bpending[:=]\s*(\d+)/i.exec(output);
|
|
234
|
+
if (m?.[1] !== undefined) {
|
|
235
|
+
return Number(m[1]);
|
|
236
|
+
}
|
|
237
|
+
// `gh pr checks` prints one row per check; count the in-progress markers.
|
|
238
|
+
// `gh pr checks` prints one row per check (`name\tpass\t1m2s\t…`). Count the
|
|
239
|
+
// in-flight rows; a table with rows but none pending IS a settle, which the
|
|
240
|
+
// first version could not see because it returned null and never counted.
|
|
241
|
+
const rows = output.match(/^\S.*\b(pending|in_progress|queued)\b/gim);
|
|
242
|
+
if (rows) {
|
|
243
|
+
return rows.length;
|
|
244
|
+
}
|
|
245
|
+
if (/\bno checks reported\b/i.test(output)) {
|
|
246
|
+
return null; // the push→registration gap — not a settle
|
|
247
|
+
}
|
|
248
|
+
if (/^\S+\s+(pass|fail|skipping|successful|failing)\b/im.test(output)) {
|
|
249
|
+
return 0; // a real result table with no in-flight rows
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Extract a tool's textual output from a post-tool payload.
|
|
256
|
+
*
|
|
257
|
+
* Returns `null` when the payload carries NO output field at all — which is not
|
|
258
|
+
* the same fact as "the command produced no output". Several platforms do not
|
|
259
|
+
* surface tool output on this event, and treating their silence as a vacuous
|
|
260
|
+
* result would turn the guard into a blanket regression that stops counting
|
|
261
|
+
* every verification everywhere. The guard only fires where it can actually
|
|
262
|
+
* read a result.
|
|
263
|
+
*/
|
|
264
|
+
function _extract_output(payload: StateDict): string | null {
|
|
265
|
+
for (const key of ["tool_response", "toolResponse", "output", "stdout", "result"]) {
|
|
266
|
+
const v = payload[key];
|
|
267
|
+
if (typeof v === "string") {
|
|
268
|
+
return v;
|
|
269
|
+
}
|
|
270
|
+
if (v !== null && typeof v === "object") {
|
|
271
|
+
return JSON.stringify(v);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
167
275
|
}
|
|
168
276
|
|
|
169
277
|
function _reset_turn(state: StateDict, session_id: string): StateDict {
|
|
@@ -171,6 +279,10 @@ function _reset_turn(state: StateDict, session_id: string): StateDict {
|
|
|
171
279
|
state["turn_started_at"] = _now();
|
|
172
280
|
state["verifications_this_turn"] = 0;
|
|
173
281
|
state["verified_this_turn"] = false;
|
|
282
|
+
// FC-3b turn-scoped counters: a CI settle must be witnessed within the same
|
|
283
|
+
// turn that claims it, so the in-flight observation does not survive a turn.
|
|
284
|
+
state["ci_saw_pending"] = false;
|
|
285
|
+
state["nonevidence_this_turn"] = 0;
|
|
174
286
|
return state;
|
|
175
287
|
}
|
|
176
288
|
|
|
@@ -205,16 +317,45 @@ function _update(state: StateDict, event: string, envelope: StateDict): StateDic
|
|
|
205
317
|
} else if (event === "post_tool_use") {
|
|
206
318
|
const [tool, cmd] = _extract_command(pl);
|
|
207
319
|
if (cmd && _is_verification(cmd)) {
|
|
320
|
+
const output = _extract_output(pl);
|
|
321
|
+
// No readable output → the guard has nothing to judge, so behaviour is
|
|
322
|
+
// exactly what it was before FC-3b landed.
|
|
323
|
+
const vacuous = output !== null && isVacuousOutput(output);
|
|
324
|
+
|
|
325
|
+
// A CI poll is evidence of a SETTLE only when this turn has already seen
|
|
326
|
+
// the run in flight. Polling once and reading `pending == 0` off a run
|
|
327
|
+
// that never registered is the measured FC-3b failure. With no readable
|
|
328
|
+
// output a poll counts for nothing — which is also its pre-FC-3b
|
|
329
|
+
// behaviour, since CI polls were not in the verification set at all.
|
|
330
|
+
let counts = !vacuous;
|
|
331
|
+
if (isCiPoll(cmd)) {
|
|
332
|
+
const pending = output === null ? null : pendingCount(output);
|
|
333
|
+
if (pending !== null && pending > 0) {
|
|
334
|
+
state["ci_saw_pending"] = true;
|
|
335
|
+
counts = false; // running, not settled
|
|
336
|
+
} else if (pending === 0) {
|
|
337
|
+
counts = !vacuous && state["ci_saw_pending"] === true;
|
|
338
|
+
} else {
|
|
339
|
+
counts = false; // unrecognised or unreadable — never read as settled
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
208
343
|
state["last_verification"] = {
|
|
209
344
|
command: cmd.slice(0, 512),
|
|
210
345
|
tool,
|
|
211
346
|
at: _now(),
|
|
212
347
|
platform: (envelope["platform"] || "") as unknown,
|
|
348
|
+
vacuous,
|
|
349
|
+
counted: counts,
|
|
213
350
|
};
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
351
|
+
if (counts) {
|
|
352
|
+
state["verifications_this_turn"] = _asInt(state["verifications_this_turn"]) + 1;
|
|
353
|
+
state["verifications_this_session"] =
|
|
354
|
+
_asInt(state["verifications_this_session"]) + 1;
|
|
355
|
+
state["verified_this_turn"] = true;
|
|
356
|
+
} else {
|
|
357
|
+
state["nonevidence_this_turn"] = _asInt(state["nonevidence_this_turn"]) + 1;
|
|
358
|
+
}
|
|
218
359
|
}
|
|
219
360
|
} else if (event === "stop") {
|
|
220
361
|
state["last_stop_at"] = _now();
|