mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-19 23:33:07 +08:00
Compare commits
28 Commits
f7035b5644
...
7ac506036c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ac506036c | ||
|
|
fb28e469f1 | ||
|
|
257aa67b61 | ||
|
|
a1cf97e3f2 | ||
|
|
10b1222fc8 | ||
|
|
cc83a85eb8 | ||
|
|
1c5c5d2389 | ||
|
|
fe49a31e9a | ||
|
|
6bced468d7 | ||
|
|
1eb7b0809d | ||
|
|
6c8e909d63 | ||
|
|
cecab59747 | ||
|
|
9e973b29fb | ||
|
|
d0303f4538 | ||
|
|
4b96af8f6a | ||
|
|
50ac061f9e | ||
|
|
4093d1bb0b | ||
|
|
714200fd20 | ||
|
|
2b387fb761 | ||
|
|
5b1a5e6433 | ||
|
|
a8e3bcb00f | ||
|
|
2d46c00763 | ||
|
|
3315f0ed61 | ||
|
|
1a7306acbe | ||
|
|
e26b5132c2 | ||
|
|
5157ee63f0 | ||
|
|
50f375bc2c | ||
|
|
bfffc33869 |
@@ -11,7 +11,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ecc",
|
"name": "ecc",
|
||||||
"source": "./",
|
"source": "./",
|
||||||
"description": "The most comprehensive Claude Code plugin — 60 agents, 229 skills, 75 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning",
|
"description": "The most comprehensive Claude Code plugin — 60 agents, 230 skills, 75 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning",
|
||||||
"version": "2.0.0-rc.1",
|
"version": "2.0.0-rc.1",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Affaan Mustafa",
|
"name": "Affaan Mustafa",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ecc",
|
"name": "ecc",
|
||||||
"version": "2.0.0-rc.1",
|
"version": "2.0.0-rc.1",
|
||||||
"description": "Battle-tested Claude Code plugin for engineering teams — 60 agents, 229 skills, 75 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use",
|
"description": "Battle-tested Claude Code plugin for engineering teams — 60 agents, 230 skills, 75 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Affaan Mustafa",
|
"name": "Affaan Mustafa",
|
||||||
"url": "https://x.com/affaanmustafa"
|
"url": "https://x.com/affaanmustafa"
|
||||||
|
|||||||
10
.github/workflows/supply-chain-watch.yml
vendored
10
.github/workflows/supply-chain-watch.yml
vendored
@@ -40,11 +40,17 @@ jobs:
|
|||||||
- name: Validate IOC scanner fixtures
|
- name: Validate IOC scanner fixtures
|
||||||
run: node tests/ci/scan-supply-chain-iocs.test.js
|
run: node tests/ci/scan-supply-chain-iocs.test.js
|
||||||
|
|
||||||
|
- name: Validate advisory source fixtures
|
||||||
|
run: node tests/ci/supply-chain-advisory-sources.test.js
|
||||||
|
|
||||||
- name: Generate IOC report
|
- name: Generate IOC report
|
||||||
run: |
|
run: |
|
||||||
mkdir -p artifacts
|
mkdir -p artifacts
|
||||||
node scripts/ci/scan-supply-chain-iocs.js --json > artifacts/supply-chain-ioc-report.json
|
node scripts/ci/scan-supply-chain-iocs.js --json > artifacts/supply-chain-ioc-report.json
|
||||||
|
|
||||||
|
- name: Generate advisory source report
|
||||||
|
run: node scripts/ci/supply-chain-advisory-sources.js --refresh --json > artifacts/supply-chain-advisory-sources.json
|
||||||
|
|
||||||
- name: Validate workflow hardening rules
|
- name: Validate workflow hardening rules
|
||||||
run: node scripts/ci/validate-workflow-security.js
|
run: node scripts/ci/validate-workflow-security.js
|
||||||
|
|
||||||
@@ -53,5 +59,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: supply-chain-ioc-report
|
name: supply-chain-ioc-report
|
||||||
path: artifacts/supply-chain-ioc-report.json
|
path: |
|
||||||
|
artifacts/supply-chain-ioc-report.json
|
||||||
|
artifacts/supply-chain-advisory-sources.json
|
||||||
retention-days: 14
|
retention-days: 14
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Everything Claude Code (ECC) — Agent Instructions
|
# Everything Claude Code (ECC) — Agent Instructions
|
||||||
|
|
||||||
This is a **production-ready AI coding plugin** providing 60 specialized agents, 229 skills, 75 commands, and automated hook workflows for software development.
|
This is a **production-ready AI coding plugin** providing 60 specialized agents, 230 skills, 75 commands, and automated hook workflows for software development.
|
||||||
|
|
||||||
**Version:** 2.0.0-rc.1
|
**Version:** 2.0.0-rc.1
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat
|
|||||||
|
|
||||||
```
|
```
|
||||||
agents/ — 60 specialized subagents
|
agents/ — 60 specialized subagents
|
||||||
skills/ — 229 workflow skills and domain knowledge
|
skills/ — 230 workflow skills and domain knowledge
|
||||||
commands/ — 75 slash commands
|
commands/ — 75 slash commands
|
||||||
hooks/ — Trigger-based automations
|
hooks/ — Trigger-based automations
|
||||||
rules/ — Always-follow guidelines (common + per-language)
|
rules/ — Always-follow guidelines (common + per-language)
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ This repo is the raw code only. The guides explain everything.
|
|||||||
### v2.0.0-rc.1 — Surface Refresh, Operator Workflows, and ECC 2.0 Alpha (Apr 2026)
|
### v2.0.0-rc.1 — Surface Refresh, Operator Workflows, and ECC 2.0 Alpha (Apr 2026)
|
||||||
|
|
||||||
- **Dashboard GUI** — New Tkinter-based desktop application (`ecc_dashboard.py` or `npm run dashboard`) with dark/light theme toggle, font customization, and project logo in header and taskbar.
|
- **Dashboard GUI** — New Tkinter-based desktop application (`ecc_dashboard.py` or `npm run dashboard`) with dark/light theme toggle, font customization, and project logo in header and taskbar.
|
||||||
- **Public surface synced to the live repo** — metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: 60 agents, 229 skills, and 75 legacy command shims.
|
- **Public surface synced to the live repo** — metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: 60 agents, 230 skills, and 75 legacy command shims.
|
||||||
- **Operator and outbound workflow expansion** — `brand-voice`, `social-graph-ranker`, `connections-optimizer`, `customer-billing-ops`, `ecc-tools-cost-audit`, `google-workspace-ops`, `project-flow-ops`, and `workspace-surface-audit` round out the operator lane.
|
- **Operator and outbound workflow expansion** — `brand-voice`, `social-graph-ranker`, `connections-optimizer`, `customer-billing-ops`, `ecc-tools-cost-audit`, `google-workspace-ops`, `project-flow-ops`, and `workspace-surface-audit` round out the operator lane.
|
||||||
- **Media and launch tooling** — `manim-video`, `remotion-video-creation`, and upgraded social publishing surfaces make technical explainers and launch content part of the same system.
|
- **Media and launch tooling** — `manim-video`, `remotion-video-creation`, and upgraded social publishing surfaces make technical explainers and launch content part of the same system.
|
||||||
- **Framework and product surface growth** — `nestjs-patterns`, richer Codex/OpenCode install surfaces, and expanded cross-harness packaging keep the repo usable beyond Claude Code alone.
|
- **Framework and product surface growth** — `nestjs-patterns`, richer Codex/OpenCode install surfaces, and expanded cross-harness packaging keep the repo usable beyond Claude Code alone.
|
||||||
@@ -393,7 +393,7 @@ If you stacked methods, clean up in this order:
|
|||||||
/plugin list ecc@ecc
|
/plugin list ecc@ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
**That's it!** You now have access to 60 agents, 229 skills, and 75 legacy command shims.
|
**That's it!** You now have access to 60 agents, 230 skills, and 75 legacy command shims.
|
||||||
|
|
||||||
### Dashboard GUI
|
### Dashboard GUI
|
||||||
|
|
||||||
@@ -1398,7 +1398,7 @@ The configuration is automatically detected from `.opencode/opencode.json`.
|
|||||||
|---------|-------------|----------|--------|
|
|---------|-------------|----------|--------|
|
||||||
| Agents | PASS: 60 agents | PASS: 12 agents | **Claude Code leads** |
|
| Agents | PASS: 60 agents | PASS: 12 agents | **Claude Code leads** |
|
||||||
| Commands | PASS: 75 commands | PASS: 35 commands | **Claude Code leads** |
|
| Commands | PASS: 75 commands | PASS: 35 commands | **Claude Code leads** |
|
||||||
| Skills | PASS: 229 skills | PASS: 37 skills | **Claude Code leads** |
|
| Skills | PASS: 230 skills | PASS: 37 skills | **Claude Code leads** |
|
||||||
| Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** |
|
| Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** |
|
||||||
| Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** |
|
| Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** |
|
||||||
| MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** |
|
| MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** |
|
||||||
@@ -1560,7 +1560,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e
|
|||||||
|---------|------------|------------|-----------|----------|----------------|
|
|---------|------------|------------|-----------|----------|----------------|
|
||||||
| **Agents** | 60 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 | N/A |
|
| **Agents** | 60 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 | N/A |
|
||||||
| **Commands** | 75 | Shared | Instruction-based | 35 | 6 prompts |
|
| **Commands** | 75 | Shared | Instruction-based | 35 | 6 prompts |
|
||||||
| **Skills** | 229 | Shared | 10 (native format) | 37 | Via instructions |
|
| **Skills** | 230 | Shared | 10 (native format) | 37 | Via instructions |
|
||||||
| **Hook Events** | 8 types | 15 types | None yet | 11 types | None |
|
| **Hook Events** | 8 types | 15 types | None yet | 11 types | None |
|
||||||
| **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks | N/A |
|
| **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks | N/A |
|
||||||
| **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions | 1 always-on file |
|
| **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions | 1 always-on file |
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
|
|||||||
/plugin list ecc@ecc
|
/plugin list ecc@ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
**完成!** 你现在可以使用 60 个代理、229 个技能和 75 个命令。
|
**完成!** 你现在可以使用 60 个代理、230 个技能和 75 个命令。
|
||||||
|
|
||||||
### multi-* 命令需要额外配置
|
### multi-* 命令需要额外配置
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ execution truth is split across:
|
|||||||
|
|
||||||
## Current Evidence
|
## Current Evidence
|
||||||
|
|
||||||
As of 2026-05-15:
|
As of 2026-05-16:
|
||||||
|
|
||||||
- GitHub queues are clean across `affaan-m/everything-claude-code`,
|
- GitHub queues are clean across `affaan-m/everything-claude-code`,
|
||||||
`affaan-m/agentshield`, `affaan-m/JARVIS`, `ECC-Tools/ECC-Tools`, and
|
`affaan-m/agentshield`, `affaan-m/JARVIS`, `ECC-Tools/ECC-Tools`, and
|
||||||
@@ -31,14 +31,24 @@ As of 2026-05-15:
|
|||||||
`ITO-59`) and five milestones: Security and Access Baseline, ECC 2.0 Preview
|
`ITO-59`) and five milestones: Security and Access Baseline, ECC 2.0 Preview
|
||||||
and Publication, AgentShield Enterprise Iteration, ECC Tools Next-Level
|
and Publication, AgentShield Enterprise Iteration, ECC Tools Next-Level
|
||||||
Platform, and Legacy Audit and Salvage.
|
Platform, and Legacy Audit and Salvage.
|
||||||
- `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-15.md` records the
|
- `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-16.md` records the
|
||||||
queue, discussion, Linear roadmap, ECC Tools access, Mini Shai-Hulud/TanStack
|
queue, discussion, Linear roadmap, ECC Tools access, Mini Shai-Hulud/TanStack
|
||||||
full-campaign follow-up, restore-only CI cache hardening, AgentShield #85
|
full-campaign follow-up, scheduled supply-chain watch coverage, no-lifecycle
|
||||||
|
CI install hardening, GitHub Actions cache purge, AgentShield #85
|
||||||
registry-signature verification, AgentShield #86 evidence-pack CI provenance,
|
registry-signature verification, AgentShield #86 evidence-pack CI provenance,
|
||||||
ECC-Tools #75 billing-gate tightening, and PR #1936 release-evidence refresh.
|
AgentShield #87 plugin-cache runtime-confidence classification, AgentShield
|
||||||
|
#88 evidence-pack inspect/readback, AgentShield #89 evidence-pack fleet
|
||||||
|
routing, ECC-Tools #75 billing-gate tightening, ECC-Tools #76 AgentShield
|
||||||
|
fleet-summary consumption, ECC-Tools #77 hosted finding evidence paths, PR
|
||||||
|
#1947 supply-chain protection, and May 16 release-evidence refresh.
|
||||||
- `npm run harness:audit -- --format json` reports 70/70 on current `main`.
|
- `npm run harness:audit -- --format json` reports 70/70 on current `main`.
|
||||||
- `npm run observability:ready` reports 21/21 readiness on current `main`,
|
- `npm run observability:ready` reports 21/21 readiness on current `main`,
|
||||||
including the GitHub/Linear/handoff/roadmap progress-sync contract.
|
including the GitHub/Linear/handoff/roadmap progress-sync contract.
|
||||||
|
- `npm run operator:dashboard -- --allow-untracked docs/drafts/ --write
|
||||||
|
docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-15.md`
|
||||||
|
now regenerates the ITO-44 prompt-to-artifact dashboard from live
|
||||||
|
`platform:audit` evidence instead of leaving the operator snapshot as a
|
||||||
|
hand-maintained document.
|
||||||
- PR #1846 merged as `797f283036904128bb1b348ae62019eb9f08cf39` and made
|
- PR #1846 merged as `797f283036904128bb1b348ae62019eb9f08cf39` and made
|
||||||
npm registry signature verification a durable workflow-security gate:
|
npm registry signature verification a durable workflow-security gate:
|
||||||
workflows that run `npm audit` now need `npm audit signatures`.
|
workflows that run `npm audit` now need `npm audit signatures`.
|
||||||
@@ -46,6 +56,12 @@ As of 2026-05-15:
|
|||||||
`docs/security/supply-chain-incident-response.md`, plus a workflow-security
|
`docs/security/supply-chain-incident-response.md`, plus a workflow-security
|
||||||
validator rule blocking `pull_request_target` workflows from restoring or
|
validator rule blocking `pull_request_target` workflows from restoring or
|
||||||
saving shared dependency caches.
|
saving shared dependency caches.
|
||||||
|
- PR #1940 merged as `6951b8d5d29d13cac6b89b461104ad03838553de` and added a
|
||||||
|
scheduled supply-chain watch workflow that emits a durable IOC report.
|
||||||
|
- PR #1941 merged as `f7035b5644ffc857879b71c39353b2141f17c3f0` and hardened
|
||||||
|
CI dependency installs against lifecycle-hook compromise by disabling package
|
||||||
|
manager lifecycle scripts, removing Actions dependency cache use, and adding
|
||||||
|
validator coverage so those patterns cannot be reintroduced silently.
|
||||||
- PR #1850 merged as `248673271455e9dc85b8add2a6ab76107b718639` and removed
|
- PR #1850 merged as `248673271455e9dc85b8add2a6ab76107b718639` and removed
|
||||||
shell access from read-only analyzer agents and zh-CN copies, reducing
|
shell access from read-only analyzer agents and zh-CN copies, reducing
|
||||||
AgentShield high findings on that surface without changing operator agents.
|
AgentShield high findings on that surface without changing operator agents.
|
||||||
@@ -74,6 +90,24 @@ As of 2026-05-15:
|
|||||||
and expanded the built-in attack corpus with an env proxy hijack scenario
|
and expanded the built-in attack corpus with an env proxy hijack scenario
|
||||||
covering proxy/runtime mutation, env-token exfiltration, DNS exfiltration,
|
covering proxy/runtime mutation, env-token exfiltration, DNS exfiltration,
|
||||||
credential-store access, and clipboard access.
|
credential-store access, and clipboard access.
|
||||||
|
- AgentShield PR #87 merged as `26bb44650663816d07180e0d20c1895e431a326c`
|
||||||
|
and added installed Claude plugin-cache runtime confidence. Cached plugin
|
||||||
|
findings now emit `runtimeConfidence: plugin-cache`, non-secret score impact
|
||||||
|
stays at the intended `0.5x`, repository-local non-Claude `plugins/cache`
|
||||||
|
paths are not downgraded, and cached hook implementations no longer appear as
|
||||||
|
active top-level `hook-code`.
|
||||||
|
- AgentShield PR #88 merged as `65ed6e2a87545dc99d962b58413f49096a4d70ec`
|
||||||
|
and added `agentshield evidence-pack inspect` for downstream consumers.
|
||||||
|
Evidence-pack bundles now have compact JSON/text readback for report score,
|
||||||
|
finding counts, runtime confidence, policy, baseline, supply-chain, CI
|
||||||
|
context, remediation phases, and malformed artifact errors without manually
|
||||||
|
opening every bundle file.
|
||||||
|
- AgentShield PR #89 merged as `521ada9091bb6d818511ab8589ae675b920c106a`
|
||||||
|
and added `agentshield evidence-pack fleet <dirs...> [--json]` for
|
||||||
|
downstream fleet routing. Multiple verified evidence packs now aggregate into
|
||||||
|
ready, security-blocker, policy-review, baseline-regression,
|
||||||
|
supply-chain-review, and invalid routes with finding, policy, baseline,
|
||||||
|
supply-chain, and remediation totals.
|
||||||
- JARVIS PR #13 merged as `127efabbfb5033ae53d7a53e1546aa3c33d6f962`
|
- JARVIS PR #13 merged as `127efabbfb5033ae53d7a53e1546aa3c33d6f962`
|
||||||
and hardened CI/deploy workflows with npm registry signature verification,
|
and hardened CI/deploy workflows with npm registry signature verification,
|
||||||
disabled persisted checkout credentials in write-permission jobs, and pinned
|
disabled persisted checkout credentials in write-permission jobs, and pinned
|
||||||
@@ -442,6 +476,16 @@ As of 2026-05-15:
|
|||||||
- ECC-Tools PR #69 extends that gate to score cached completed hosted job
|
- ECC-Tools PR #69 extends that gate to score cached completed hosted job
|
||||||
outputs for the current PR head, so hosted artifacts can satisfy corpus
|
outputs for the current PR head, so hosted artifacts can satisfy corpus
|
||||||
evidence expectations before the check reports a promotion gap.
|
evidence expectations before the check reports a promotion gap.
|
||||||
|
- ECC-Tools PR #76 consumes AgentShield PR #89 fleet output in hosted security
|
||||||
|
review: `agentshield-evidence/fleet-summary.json` is now classified as
|
||||||
|
`evidence-pack-fleet`, invalid packs and security-blocker routes become
|
||||||
|
high-severity hosted findings, and policy, baseline, and supply-chain routes
|
||||||
|
produce owner-ready review findings.
|
||||||
|
- ECC-Tools PR #77 merged as `31fd883b3f0cee135aee4839b01d34855b7867f6`
|
||||||
|
and adds an `Evidence` column to hosted job PR comments and check-run
|
||||||
|
details, surfacing up to three source evidence paths for each finding so
|
||||||
|
AgentShield fleet-derived findings point operators back to the exact bundle
|
||||||
|
artifact.
|
||||||
- ECC PR #1803 landed the contributor Quarkus handling branch after maintainer
|
- ECC PR #1803 landed the contributor Quarkus handling branch after maintainer
|
||||||
cleanup, current-`main` alignment, full local validation, and preservation of
|
cleanup, current-`main` alignment, full local validation, and preservation of
|
||||||
the author's removal of incomplete ja-JP and zh-CN Quarkus translations.
|
the author's removal of incomplete ja-JP and zh-CN Quarkus translations.
|
||||||
@@ -494,11 +538,11 @@ is not complete unless the evidence column exists and has been freshly verified.
|
|||||||
| Naming and rename readiness | Naming matrix across package/plugin/docs/social surfaces | `docs/releases/2.0.0-rc.1/naming-and-publication-matrix.md` records current package, repo, Claude plugin, Codex plugin, OpenCode, and npm availability evidence | Complete for rc.1; post-rc rename remains future work |
|
| Naming and rename readiness | Naming matrix across package/plugin/docs/social surfaces | `docs/releases/2.0.0-rc.1/naming-and-publication-matrix.md` records current package, repo, Claude plugin, Codex plugin, OpenCode, and npm availability evidence | Complete for rc.1; post-rc rename remains future work |
|
||||||
| Claude and Codex plugin publication | Contact/submission path with required artifacts and status | Publication readiness, naming matrix, and May 12 dry-run evidence document plugin validation, clean-checkout Claude tag/install smoke, and Codex marketplace CLI shape | Needs explicit approval for real tag/push and marketplace submission |
|
| Claude and Codex plugin publication | Contact/submission path with required artifacts and status | Publication readiness, naming matrix, and May 12 dry-run evidence document plugin validation, clean-checkout Claude tag/install smoke, and Codex marketplace CLI shape | Needs explicit approval for real tag/push and marketplace submission |
|
||||||
| Articles, tweets, and announcements | X thread, LinkedIn copy, GitHub release copy, push checklist | Draft launch collateral exists under rc.1 release docs | Needs URL-backed refresh |
|
| Articles, tweets, and announcements | X thread, LinkedIn copy, GitHub release copy, push checklist | Draft launch collateral exists under rc.1 release docs | Needs URL-backed refresh |
|
||||||
| AgentShield enterprise iteration | Policy gates, SARIF, packs, provenance, corpus, HTML reports, exception lifecycle audit, baseline drift Action/CLI surfaces, evidence-pack redaction, harness adapter registry, enterprise research roadmap, supply-chain hardened release path, CI-safe baseline fingerprints, corpus accuracy recommendations, remediation workflow phases, env proxy hijack corpus coverage, Mini Shai-Hulud full-campaign package IOCs, and CI-provenance evidence packs | PRs #53, #55-#64, #67-#69, and #78-#86 landed with test evidence; native PDF export deferred in favor of self-contained HTML plus print-to-PDF until explicit enterprise demand appears; `docs/architecture/agentshield-enterprise-research-roadmap.md` now has baseline drift, evidence-pack bundle, redaction, adapter-registry, supply-chain hardening, hashed baseline fingerprints, corpus accuracy recommendation, remediation workflow, env proxy hijack corpus, Mini Shai-Hulud full-campaign package-table, and `ci-context.json` provenance slices landed | Next evidence-pack consumer/readback workflow depth |
|
| AgentShield enterprise iteration | Policy gates, SARIF, packs, provenance, corpus, HTML reports, exception lifecycle audit, baseline drift Action/CLI surfaces, evidence-pack redaction, harness adapter registry, enterprise research roadmap, supply-chain hardened release path, CI-safe baseline fingerprints, corpus accuracy recommendations, remediation workflow phases, env proxy hijack corpus coverage, Mini Shai-Hulud full-campaign package IOCs, CI-provenance evidence packs, plugin-cache runtime-confidence triage, evidence-pack consumer readback, and fleet-level evidence-pack routing | PRs #53, #55-#64, #67-#69, and #78-#89 landed with test evidence, ECC-Tools #76 consumes the fleet-summary output in hosted security review, and ECC-Tools #77 surfaces source evidence paths in hosted finding output; native PDF export deferred in favor of self-contained HTML plus print-to-PDF until explicit enterprise demand appears; `docs/architecture/agentshield-enterprise-research-roadmap.md` now has baseline drift, evidence-pack bundle, redaction, adapter-registry, supply-chain hardening, hashed baseline fingerprints, corpus accuracy recommendation, remediation workflow, env proxy hijack corpus, Mini Shai-Hulud full-campaign package-table, `ci-context.json` provenance, `plugin-cache` confidence, `evidence-pack inspect` readback, and `evidence-pack fleet` routing slices landed | Next cross-harness policy integration and richer fleet-review workflow |
|
||||||
| ECC Tools next-level app | Billing audit, PR checks, deep analyzer, sync backlog, evaluator/RAG corpus, analysis-depth readiness, hosted execution planning, hosted CI diagnostics, hosted security evidence review, hosted harness compatibility audit, hosted reference-set evaluation, hosted AI routing/cost review, hosted team backlog routing, hosted depth-plan check-run, PR-comment hosted job dispatch, hosted job result history/check-runs, hosted result status command, status-aware depth-plan recommendations, hosted promotion readiness, hosted promotion output scoring, hosted promotion retrieval planning, hosted promotion judge contract, gated hosted promotion judge execution, payment-announcement readiness | PRs #26-#43 plus #53-#74 landed with test evidence, including AgentShield evidence-pack gap routing, canonical bundle recognition, supply-chain signature gates, PR draft follow-up Linear tracking, evidence-backed/deep-ready repository classification, the `/api/analysis/depth-plan` hosted job plan, `/api/analysis/jobs/ci-diagnostics`, `/api/analysis/jobs/security-evidence-review`, `/api/analysis/jobs/harness-compatibility-audit`, `/api/analysis/jobs/reference-set-evaluation`, `/api/analysis/jobs/ai-routing-cost-review`, `/api/analysis/jobs/team-backlog-routing`, the `ECC Tools / Hosted Depth Plan` check-run, `/ecc-tools analyze --job ...` PR-comment dispatch, non-blocking per-hosted-job result check-runs backed by 30-day result cache records, `/ecc-tools analyze --job status` cache lookup, cache-aware next-job recommendations in the depth-plan check-run, the `ECC Tools / Hosted Promotion Readiness` corpus-backed PR check-run, deterministic hosted-output scoring against cached completed job artifacts/findings, ranked retrieval/model-prompt planning, the fail-closed `hosted-promotion-judge.v1` request contract, opt-in live model-judge execution behind hosted evidence, entitlement, budget, provider, executor, strict JSON, and citation gates, a fail-closed `/api/billing/readiness` `announcementGate` for native GitHub payments claims, and `npm run billing:announcement-gate` as the non-secret operator verifier | Next work is hosted promotion telemetry, operator review UX, and live Marketplace test-account readback |
|
| ECC Tools next-level app | Billing audit, PR checks, deep analyzer, sync backlog, evaluator/RAG corpus, analysis-depth readiness, hosted execution planning, hosted CI diagnostics, hosted security evidence review, hosted harness compatibility audit, hosted reference-set evaluation, hosted AI routing/cost review, hosted team backlog routing, hosted depth-plan check-run, PR-comment hosted job dispatch, hosted job result history/check-runs, hosted result status command, status-aware depth-plan recommendations, hosted promotion readiness, hosted promotion output scoring, hosted promotion retrieval planning, hosted promotion judge contract, gated hosted promotion judge execution, payment-announcement readiness, AgentShield fleet-summary hosted routing, and hosted finding source-evidence surfacing | PRs #26-#43 plus #53-#77 landed with test evidence, including AgentShield evidence-pack gap routing, canonical bundle recognition, supply-chain signature gates, PR draft follow-up Linear tracking, evidence-backed/deep-ready repository classification, the `/api/analysis/depth-plan` hosted job plan, `/api/analysis/jobs/ci-diagnostics`, `/api/analysis/jobs/security-evidence-review`, `/api/analysis/jobs/harness-compatibility-audit`, `/api/analysis/jobs/reference-set-evaluation`, `/api/analysis/jobs/ai-routing-cost-review`, `/api/analysis/jobs/team-backlog-routing`, the `ECC Tools / Hosted Depth Plan` check-run, `/ecc-tools analyze --job ...` PR-comment dispatch, non-blocking per-hosted-job result check-runs backed by 30-day result cache records, `/ecc-tools analyze --job status` cache lookup, cache-aware next-job recommendations in the depth-plan check-run, the `ECC Tools / Hosted Promotion Readiness` corpus-backed PR check-run, deterministic hosted-output scoring against cached completed job artifacts/findings, ranked retrieval/model-prompt planning, the fail-closed `hosted-promotion-judge.v1` request contract, opt-in live model-judge execution behind hosted evidence, entitlement, budget, provider, executor, strict JSON, and citation gates, a fail-closed `/api/billing/readiness` `announcementGate` for native GitHub payments claims, `npm run billing:announcement-gate` as the non-secret operator verifier, hosted security findings for AgentShield fleet summaries, and an `Evidence` column in hosted finding comments/check-runs | Next work is hosted promotion telemetry, richer operator review UX, and live Marketplace test-account readback |
|
||||||
| GitGuardian/Dependabot/CodeRabbit-style checks | Non-blocking taxonomy, deterministic follow-up checks, and local supply-chain gates | ECC-Tools risk taxonomy check plus follow-up signals landed, including Skill Quality, Deep Analyzer Evidence, Analyzer Corpus Evidence, RAG/Evaluator Evidence, PR Review/Salvage Evidence, and AgentShield evidence-pack evidence; #1846 added npm registry signature gates; #1848 added the supply-chain incident-response playbook and `pull_request_target` cache-poisoning validator guard; #1851 added the privileged checkout credential-persistence guard; AgentShield #78, JARVIS #13, and ECC-Tools #53 applied the same hardening outside trunk | Current supply-chain gate complete; deeper hosted review features remain future |
|
| GitGuardian/Dependabot/CodeRabbit-style checks | Non-blocking taxonomy, deterministic follow-up checks, and local supply-chain gates | ECC-Tools risk taxonomy check plus follow-up signals landed, including Skill Quality, Deep Analyzer Evidence, Analyzer Corpus Evidence, RAG/Evaluator Evidence, PR Review/Salvage Evidence, and AgentShield evidence-pack evidence; #1846 added npm registry signature gates; #1848 added the supply-chain incident-response playbook and `pull_request_target` cache-poisoning validator guard; #1851 added the privileged checkout credential-persistence guard; AgentShield #78, JARVIS #13, and ECC-Tools #53 applied the same hardening outside trunk | Current supply-chain gate complete; deeper hosted review features remain future |
|
||||||
| Harness-agnostic learning system | Audit, adapter matrix, observability, traces, promotion loop | Audit/adapters/observability gates plus `docs/architecture/evaluator-rag-prototype.md`, `examples/evaluator-rag-prototype/`, and ECC-Tools PR #40 define read-only stale-salvage, billing-readiness, CI-failure-diagnosis, harness-config-quality, AgentShield policy-exception, skill-quality evidence, deep-analyzer evidence, and RAG/evaluator comparison scenarios with trace, report, playbook, verifier, and predictive-check artifacts; ECC-Tools PRs #68-#72 now turn that corpus into a deterministic PR check-run gate with cached hosted-output scoring, ranked retrieval candidates, a model prompt seed, a fail-closed hosted model-judge request contract, and opt-in live model execution behind strict hosted-evidence gates | Deterministic hosted PR check, cached output scoring, retrieval planning, judge contract, and gated model execution integrated |
|
| Harness-agnostic learning system | Audit, adapter matrix, observability, traces, promotion loop | Audit/adapters/observability gates plus `docs/architecture/evaluator-rag-prototype.md`, `examples/evaluator-rag-prototype/`, and ECC-Tools PR #40 define read-only stale-salvage, billing-readiness, CI-failure-diagnosis, harness-config-quality, AgentShield policy-exception, skill-quality evidence, deep-analyzer evidence, and RAG/evaluator comparison scenarios with trace, report, playbook, verifier, and predictive-check artifacts; ECC-Tools PRs #68-#72 now turn that corpus into a deterministic PR check-run gate with cached hosted-output scoring, ranked retrieval candidates, a model prompt seed, a fail-closed hosted model-judge request contract, and opt-in live model execution behind strict hosted-evidence gates | Deterministic hosted PR check, cached output scoring, retrieval planning, judge contract, and gated model execution integrated |
|
||||||
| Linear roadmap is detailed | Linear project status plus repo mirror | Repo mirror exists; issue creation was retried on 2026-05-12 and remains blocked by the workspace free issue limit; this May 15 sync adds ECC #1860, AgentShield #78-#86, JARVIS #13, ECC-Tools #53-#74, resolved queue/discussion counts, and notes that Linear connector status updates after ECC-Tools #68 remain blocked by a connector secret-owner error | Needs recurring status updates after connector recovery |
|
| Linear roadmap is detailed | Linear project status plus repo mirror | Repo mirror exists; issue creation was retried on 2026-05-12 and remains blocked by the workspace free issue limit; this May 16 sync adds ECC #1860, AgentShield #78-#89, JARVIS #13, ECC-Tools #53-#77, resolved queue/discussion counts, and a generated `operator:dashboard` prompt-to-artifact audit for recurring status updates | Needs recurring status updates after each significant merge batch |
|
||||||
| Flow separation and progress tracking | Flow lanes with owner artifacts and update cadence | This roadmap defines lanes below and `docs/architecture/progress-sync-contract.md` makes GitHub/Linear/handoff/roadmap sync part of the readiness gate | Active |
|
| Flow separation and progress tracking | Flow lanes with owner artifacts and update cadence | This roadmap defines lanes below and `docs/architecture/progress-sync-contract.md` makes GitHub/Linear/handoff/roadmap sync part of the readiness gate | Active |
|
||||||
| Realtime Linear sync | Project updates while issue limit is blocked; issues later | ECC-Tools #39 implements opt-in Linear API sync for deferred follow-up backlog items, and ECC-Tools #54 adds copy-ready PR drafts to that backlog when draft PR shells are not opened; `docs/architecture/progress-sync-contract.md` defines the local file-backed realtime boundary while issue capacity is blocked | Needs workspace capacity/config rollout |
|
| Realtime Linear sync | Project updates while issue limit is blocked; issues later | ECC-Tools #39 implements opt-in Linear API sync for deferred follow-up backlog items, and ECC-Tools #54 adds copy-ready PR drafts to that backlog when draft PR shells are not opened; `docs/architecture/progress-sync-contract.md` defines the local file-backed realtime boundary while issue capacity is blocked | Needs workspace capacity/config rollout |
|
||||||
| Observability for self-use | Local readiness gate, traces, status snapshots, HUD/status contract, risk ledger, progress-sync contract | `npm run observability:ready` reports 21/21 | Complete for local gate |
|
| Observability for self-use | Local readiness gate, traces, status snapshots, HUD/status contract, risk ledger, progress-sync contract | `npm run observability:ready` reports 21/21 | Complete for local gate |
|
||||||
@@ -518,9 +562,9 @@ repo evidence and merge commits.
|
|||||||
| Release and publication | rc.1 release docs, publication readiness doc | Naming matrix and plugin submission/contact checklist | Before any tag |
|
| Release and publication | rc.1 release docs, publication readiness doc | Naming matrix and plugin submission/contact checklist | Before any tag |
|
||||||
| Harness OS core | Audit, adapter matrix, observability docs, `ecc2/` | HUD/session-control acceptance spec | Weekly until GA |
|
| Harness OS core | Audit, adapter matrix, observability docs, `ecc2/` | HUD/session-control acceptance spec | Weekly until GA |
|
||||||
| Evaluation and RAG | Reference-set validation, harness audit, traces, ECC-Tools corpus | Read-only evaluator/RAG prototype plus stale-salvage, billing-readiness, CI-failure-diagnosis, harness-config-quality, AgentShield policy-exception, skill-quality evidence, deep-analyzer evidence, and RAG/evaluator comparison fixtures; ECC-Tools #68 publishes the corpus as a hosted promotion readiness check-run, #69 scores cached hosted job outputs against the same corpus, #70 emits ranked retrieval candidates plus a model prompt seed, #71 adds a fail-closed hosted model-judge request contract, and #72 executes that judge only when explicitly enabled and backed by hosted retrieval citations | Hosted promotion telemetry and operator review UX |
|
| Evaluation and RAG | Reference-set validation, harness audit, traces, ECC-Tools corpus | Read-only evaluator/RAG prototype plus stale-salvage, billing-readiness, CI-failure-diagnosis, harness-config-quality, AgentShield policy-exception, skill-quality evidence, deep-analyzer evidence, and RAG/evaluator comparison fixtures; ECC-Tools #68 publishes the corpus as a hosted promotion readiness check-run, #69 scores cached hosted job outputs against the same corpus, #70 emits ranked retrieval candidates plus a model prompt seed, #71 adds a fail-closed hosted model-judge request contract, and #72 executes that judge only when explicitly enabled and backed by hosted retrieval citations | Hosted promotion telemetry and operator review UX |
|
||||||
| AgentShield enterprise | AgentShield PR evidence and roadmap notes | Remediation workflow depth or corpus expansion follow-up | Next implementation batch |
|
| AgentShield enterprise | AgentShield PR evidence and roadmap notes | Fleet routing landed in #89 after evidence-pack inspect/readback shipped in #88; ECC-Tools #76 consumes fleet summaries and #77 surfaces source evidence paths in hosted findings | Cross-harness policy integration and richer fleet-review workflow |
|
||||||
| ECC Tools app | ECC-Tools PR evidence, billing audit, risk taxonomy, evaluator/RAG corpus | ECC-Tools #53 published the supply-chain workflow hardening branch, #54 tracks copy-ready PR drafts in the Linear/project backlog, #55 classifies analysis-depth readiness, #56 exposes the hosted execution plan, #57 executes the first hosted CI diagnostics job, #58 executes the hosted security evidence review job, #59 executes the hosted harness compatibility audit, #60 executes the hosted reference-set evaluation, #61 executes the hosted AI routing/cost review, #62 executes hosted team backlog routing, #63 publishes the hosted depth-plan check-run, #64 dispatches hosted jobs from PR comments, #65 persists hosted result history/check-runs, #66 exposes hosted job status from PR comments, #67 makes depth-plan recommendations cache-aware, #68 publishes hosted promotion readiness from the evaluator/RAG corpus, #69 scores cached hosted job outputs against that corpus, #70 emits ranked retrieval candidates plus a model prompt seed, #71 emits the gated `hosted-promotion-judge.v1` contract without live model calls, #72 adds opt-in live model-judge execution behind hosted-evidence and strict JSON/citation gates, #73 adds a fail-closed native-payments `announcementGate` to billing readiness, and #74 adds `npm run billing:announcement-gate` for operator verification | Live Marketplace test-account readback and hosted promotion telemetry |
|
| ECC Tools app | ECC-Tools PR evidence, billing audit, risk taxonomy, evaluator/RAG corpus | ECC-Tools #53 published the supply-chain workflow hardening branch, #54 tracks copy-ready PR drafts in the Linear/project backlog, #55 classifies analysis-depth readiness, #56 exposes the hosted execution plan, #57 executes the first hosted CI diagnostics job, #58 executes the hosted security evidence review job, #59 executes the hosted harness compatibility audit, #60 executes the hosted reference-set evaluation, #61 executes the hosted AI routing/cost review, #62 executes hosted team backlog routing, #63 publishes the hosted depth-plan check-run, #64 dispatches hosted jobs from PR comments, #65 persists hosted result history/check-runs, #66 exposes hosted job status from PR comments, #67 makes depth-plan recommendations cache-aware, #68 publishes hosted promotion readiness from the evaluator/RAG corpus, #69 scores cached hosted job outputs against that corpus, #70 emits ranked retrieval candidates plus a model prompt seed, #71 emits the gated `hosted-promotion-judge.v1` contract without live model calls, #72 adds opt-in live model-judge execution behind hosted-evidence and strict JSON/citation gates, #73 adds a fail-closed native-payments `announcementGate` to billing readiness, #74 adds `npm run billing:announcement-gate` for operator verification, #75 tightens the billing announcement gate for live Marketplace readback, #76 routes AgentShield fleet-summary evidence into hosted security findings, and #77 adds source evidence paths to hosted finding output | Live Marketplace test-account readback, hosted promotion telemetry, and richer operator review UX |
|
||||||
| Linear progress | Linear project status updates, `docs/architecture/progress-sync-contract.md`, and this mirror | Status update with queue/evidence/missing gates | Every significant merge batch |
|
| Linear progress | Linear project status updates, `docs/architecture/progress-sync-contract.md`, generated `operator:dashboard` output, and this mirror | Status update with queue/evidence/missing gates | Every significant merge batch |
|
||||||
|
|
||||||
The project status update should always include:
|
The project status update should always include:
|
||||||
|
|
||||||
@@ -737,8 +781,15 @@ Acceptance:
|
|||||||
exfiltration; PRs #83-#85 hardened Mini Shai-Hulud IOC coverage and
|
exfiltration; PRs #83-#85 hardened Mini Shai-Hulud IOC coverage and
|
||||||
release-path supply-chain verification; PR #86 added whitelisted
|
release-path supply-chain verification; PR #86 added whitelisted
|
||||||
`ci-context.json` workflow, commit, run, and runtime provenance to evidence
|
`ci-context.json` workflow, commit, run, and runtime provenance to evidence
|
||||||
packs; and ECC-Tools PRs #42/#43 now route and recognize evidence packs.
|
packs; PR #87 classified installed Claude plugin caches separately from
|
||||||
The next slice is evidence-pack consumer/readback workflow depth.
|
active top-level runtime config, including cached hook implementations; PR
|
||||||
|
#88 added `agentshield evidence-pack inspect` JSON/text readback for
|
||||||
|
downstream consumers; PR #89 added `agentshield evidence-pack fleet`
|
||||||
|
summary/routing across multiple inspected bundles; ECC-Tools PRs #42/#43 now
|
||||||
|
route and recognize evidence packs; ECC-Tools PR #76 consumes fleet
|
||||||
|
summaries in hosted security review; and ECC-Tools PR #77 surfaces source
|
||||||
|
evidence paths in hosted PR comments and check-runs. The next slice is
|
||||||
|
cross-harness policy integration and richer fleet-review workflow.
|
||||||
2. Run ECC-Tools `/api/billing/readiness` against a Marketplace-managed test
|
2. Run ECC-Tools `/api/billing/readiness` against a Marketplace-managed test
|
||||||
account and require `announcementGate.ready === true` before any native
|
account and require `announcementGate.ready === true` before any native
|
||||||
GitHub payments announcement.
|
GitHub payments announcement.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# AgentShield Enterprise Research Roadmap
|
# AgentShield Enterprise Research Roadmap
|
||||||
|
|
||||||
Generated: 2026-05-12
|
Generated: 2026-05-12; refreshed with May 16 AgentShield PR #87, #88, and #89 evidence.
|
||||||
|
|
||||||
This is a planning artifact for the next AgentShield enterprise iteration. It
|
This is a planning artifact for the next AgentShield enterprise iteration. It
|
||||||
does not modify AgentShield code. The goal is to turn the current scanner,
|
does not modify AgentShield code. The goal is to turn the current scanner,
|
||||||
@@ -84,12 +84,44 @@ AgentShield is already more than a static lint tool:
|
|||||||
- Enterprise hooks exist: policy packs, exception metadata, expiring/expired
|
- Enterprise hooks exist: policy packs, exception metadata, expiring/expired
|
||||||
exception reporting, SARIF code scanning, and job-summary output.
|
exception reporting, SARIF code scanning, and job-summary output.
|
||||||
- Accuracy work is active: `runtimeConfidence`, template/example weighting,
|
- Accuracy work is active: `runtimeConfidence`, template/example weighting,
|
||||||
docs-example downgrades, hook-manifest resolution, false-positive audit
|
docs-example downgrades, installed Claude plugin-cache confidence,
|
||||||
guidance, and corpus readiness.
|
hook-manifest resolution, false-positive audit guidance, and corpus readiness.
|
||||||
|
- Evidence-pack consumption is now first-class enough for downstream tools:
|
||||||
|
`agentshield evidence-pack inspect` verifies a bundle and emits compact
|
||||||
|
JSON/text summaries for report score, finding counts, runtime confidence,
|
||||||
|
policy, baseline, supply-chain, CI context, remediation, and malformed
|
||||||
|
artifact errors.
|
||||||
|
- Fleet-level evidence-pack consumption now has a local routing primitive:
|
||||||
|
`agentshield evidence-pack fleet <dirs...> [--json]` aggregates multiple
|
||||||
|
inspected bundles into ready, security-blocker, policy-review,
|
||||||
|
baseline-regression, supply-chain-review, and invalid routes.
|
||||||
|
- ECC-Tools now consumes that fleet primitive in hosted security review:
|
||||||
|
`agentshield-evidence/fleet-summary.json` routes invalid packs, security
|
||||||
|
blockers, policy reviews, baseline regressions, and supply-chain reviews into
|
||||||
|
hosted findings.
|
||||||
|
|
||||||
The next iteration should not be "add more regex rules" by default. The higher
|
May 16 update: AgentShield PR #87 merged as
|
||||||
leverage move is to make AgentShield remember, compare, route, and enforce
|
`26bb44650663816d07180e0d20c1895e431a326c`. It classifies installed Claude
|
||||||
security posture across time, repos, teams, and harnesses.
|
plugin cache content as `runtimeConfidence: plugin-cache`, keeps non-secret
|
||||||
|
plugin-cache score impact at `0.5x`, avoids downgrading repository-local
|
||||||
|
non-Claude `plugins/cache` paths, and makes plugin-cache classification win
|
||||||
|
before cached hook implementations would otherwise appear as active `hook-code`.
|
||||||
|
AgentShield PR #88 merged as
|
||||||
|
`65ed6e2a87545dc99d962b58413f49096a4d70ec`. It adds
|
||||||
|
`agentshield evidence-pack inspect <dir> [--json]`, validates the bundle before
|
||||||
|
readback, summarizes every consumer-facing evidence artifact, and keeps
|
||||||
|
malformed-but-valid JSON artifacts from crashing inspection.
|
||||||
|
AgentShield PR #89 merged as
|
||||||
|
`521ada9091bb6d818511ab8589ae675b920c106a`. It adds
|
||||||
|
`agentshield evidence-pack fleet <dirs...> [--json]`, verifies each pack through
|
||||||
|
the inspect path, aggregates finding, policy, baseline, supply-chain, and
|
||||||
|
remediation totals, and assigns each pack to a deterministic fleet route.
|
||||||
|
|
||||||
|
The next iteration after fleet routing should not be "add more regex rules" by
|
||||||
|
default. ECC-Tools follow-up routing now consumes fleet summaries and surfaces
|
||||||
|
source evidence paths in hosted findings; the higher leverage move is
|
||||||
|
cross-harness policy integration plus richer review workflow for routed fleet
|
||||||
|
findings.
|
||||||
|
|
||||||
## Enterprise Gaps
|
## Enterprise Gaps
|
||||||
|
|
||||||
@@ -323,6 +355,8 @@ The AgentShield enterprise iteration is not complete until these are true:
|
|||||||
- Built CLI smoke tests cover the new flags or report modes.
|
- Built CLI smoke tests cover the new flags or report modes.
|
||||||
- GitHub Action self-test covers the new CI-visible output.
|
- GitHub Action self-test covers the new CI-visible output.
|
||||||
- Documentation names the free/local path and the paid/team path separately.
|
- Documentation names the free/local path and the paid/team path separately.
|
||||||
|
- Runtime-confidence changes include live scan evidence proving lower-confidence
|
||||||
|
plugin/package surfaces stay visible instead of being suppressed.
|
||||||
- Evidence produced by the feature is deterministic enough for CI diffing.
|
- Evidence produced by the feature is deterministic enough for CI diffing.
|
||||||
- ECC-Tools can consume the finding fingerprints or backlog export without
|
- ECC-Tools can consume the finding fingerprints or backlog export without
|
||||||
exceeding GitHub/Linear object caps.
|
exceeding GitHub/Linear object caps.
|
||||||
|
|||||||
@@ -1,114 +1,55 @@
|
|||||||
# ECC Operator Readiness Dashboard - 2026-05-15
|
# ECC Operator Readiness Dashboard
|
||||||
|
|
||||||
This dashboard is an operator snapshot, not a release approval. Use it to decide
|
This dashboard is generated by `npm run operator:dashboard`. It is an operator snapshot, not release approval.
|
||||||
the next ECC 2.0 work batch and to keep Linear, GitHub, and repo evidence in
|
|
||||||
sync. Before publishing, repeat the checks from the final release commit in a
|
Generated: 2026-05-16T05:49:52.579Z
|
||||||
clean checkout.
|
Commit: fb28e469f1b94d18c9196f1cac9dabbf08286ef3
|
||||||
|
Status: work remaining
|
||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
|
|
||||||
| Area | Status | Evidence |
|
| Area | Status | Evidence |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| PR queue | Current | 0 open PRs across checked repos |
|
| PR queue | Current | 0 open PRs across tracked repos |
|
||||||
| Issue queue | Current | 0 open issues across checked repos |
|
| Issue queue | Current | 0 open issues across tracked repos |
|
||||||
| Discussions | Current | 58 main-repo discussions; 0 need maintainer touch; 0 answerable discussions missing accepted answers |
|
| Discussions | Current | 0 need maintainer touch; 0 missing accepted answer |
|
||||||
| Local worktree | Current with caveat | `main...origin/main`; unrelated `?? docs/drafts/` ignored |
|
| Local worktree | Current | 0 blocking dirty files; 1 ignored dirty entries |
|
||||||
| Security sweep | Current with follow-up | IOC scan, audits, package-manager hardening, and scheduled watch workflow completed |
|
| Dashboard generation | Current | platform audit ready: true; GitHub skipped: false |
|
||||||
| Linear roadmap | Current with follow-up | `ECC Platform Roadmap`, ITO-44 through ITO-59 |
|
| Publication | Not complete | release, npm, plugin, billing, and announcement gates are tracked below |
|
||||||
| ECC 2.0 publication | Not complete | Release, npm, plugin, and announcement gates pending |
|
|
||||||
| AgentShield enterprise depth | In progress | AgentShield #86 merged; live IOC loop still pending |
|
|
||||||
| ECC Tools next-level app | In progress | Billing announcement gate merged; live readback pending |
|
|
||||||
| Legacy audit and salvage | Not complete | ITO-55 remains open |
|
|
||||||
|
|
||||||
## Live Command Evidence
|
|
||||||
|
|
||||||
Run these from `everything-claude-code` unless a row says otherwise.
|
|
||||||
|
|
||||||
| Evidence | Command | 2026-05-15 result |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Platform audit | `node scripts/platform-audit.js --json --allow-untracked docs/drafts/` | `ready: true`; open PRs 0/20; open issues 0/20; discussions needing maintainer touch 0; answerable discussions missing accepted answers 0; blocking dirty files 0 |
|
|
||||||
| Discussion audit | `node scripts/discussion-audit.js --json --repo affaan-m/everything-claude-code` | `ready: true`; 58 discussions sampled; 0 need maintainer touch; 0 answerable discussions missing accepted answers |
|
|
||||||
| Main repo status | `git status --short --branch` | `## main...origin/main`; `?? docs/drafts/` remains unrelated |
|
|
||||||
| Main commit | `git rev-parse HEAD` | `6887f2952d193cff10b3eb79af7765555d8ca9f5` |
|
|
||||||
| Main repo PRs/issues | GitHub connector and `gh` readback | 0 open PRs; 0 open issues |
|
|
||||||
| AgentShield PRs/issues | GitHub connector and `gh` readback | 0 open PRs; 0 open issues |
|
|
||||||
| ECC Tools PRs/issues | Local `gh pr list` and `gh issue list` | 0 open PRs; 0 open issues |
|
|
||||||
| Discussion baseline | GraphQL discussion sweep | Main repo #1923 marked answered; no answerable Q&A missing an answer |
|
|
||||||
| Supply-chain IOC scan | `node scripts/ci/scan-supply-chain-iocs.js --root <ECC-workspace> --home` | Passed; repo/home targeted scan inspected 200 files after clean no-script reinstall |
|
|
||||||
| IOC unit tests | `node tests/ci/scan-supply-chain-iocs.test.js` | 15/15 passed |
|
|
||||||
| Dead-man switch persistence sweep | Process, LaunchAgent, and known payload filename sweep for Mini Shai-Hulud markers | No matches |
|
|
||||||
| Workflow security gate | `node scripts/ci/validate-workflow-security.js` | Passed; 8 workflow files inspected; package-manager test installs disable lifecycle scripts and no Actions cache use remains |
|
|
||||||
| Supply-chain watch workflow | `.github/workflows/supply-chain-watch.yml` | Scheduled every 6 hours; emits `supply-chain-ioc-report.json` |
|
|
||||||
| npm signatures and audit | `npm audit signatures && npm audit --audit-level=high` in main | 213 verified signatures, 17 verified attestations, 0 high vulnerabilities |
|
|
||||||
|
|
||||||
## Prompt-To-Artifact Checklist
|
## Prompt-To-Artifact Checklist
|
||||||
|
|
||||||
| Objective requirement | Artifact or evidence | Status | Gap |
|
| Objective requirement | Artifact or gate | Status | Evidence | Gap |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| Keep PRs under 20 | `scripts/platform-audit.js`; live GitHub readback | Current | Repeat before release |
|
| Keep public PRs below 20 | scripts/platform-audit.js live GitHub sweep | current | 0 open PRs across 5 tracked repos | repeat before release |
|
||||||
| Keep issues under 20 | `scripts/platform-audit.js`; live GitHub readback | Current | Repeat before release |
|
| Keep public issues below 20 | scripts/platform-audit.js live GitHub sweep | current | 0 open issues across 5 tracked repos | repeat before release |
|
||||||
| Respond and manage discussions | `scripts/discussion-audit.js`; #1923 answer marked | Current | Repeat before release |
|
| Respond and manage repository discussions | scripts/platform-audit.js discussion summary | current | 0 need maintainer touch; 0 answerable discussions missing accepted answer | repeat before release |
|
||||||
| ECC 2.0 preview pack ready | `preview-pack-manifest.md`; `publication-readiness.md` | In progress | Final publish evidence still pending |
|
| Build ITO-44 completion dashboard into a repeatable command | npm run operator:dashboard | complete | operator:dashboard package script exists | keep generated dashboard attached to publication evidence |
|
||||||
| Include Hermes specialized skills | `docs/HERMES-SETUP.md`; `skills/hermes-imports/SKILL.md` | In progress | Final preview-pack smoke still pending |
|
| ECC 2.0 preview pack ready | docs/releases/2.0.0-rc.1/preview-pack-manifest.md | in_progress | preview pack manifest is in-tree | final clean-checkout release approval and publish evidence still pending |
|
||||||
| Name-change and availability path | `naming-and-publication-matrix.md`; ITO-46 | In progress | Final name/package/channel choice not approved |
|
| Include Hermes specialized skills safely | docs/HERMES-SETUP.md and skills/hermes-imports/SKILL.md | in_progress | Hermes setup and import skill are present | final preview-pack smoke and release review pending |
|
||||||
| Claude plugin publication path | `.claude-plugin/`; `publication-readiness.md`; ITO-46 | In progress | Actual publication still pending |
|
| Prepare name-change, Claude plugin, and Codex plugin paths | naming-and-publication-matrix plus publication-readiness | in_progress | naming matrix and plugin readiness gates exist | real tag/push, marketplace submission, and final channel choice remain approval-gated |
|
||||||
| Codex plugin publication path | `.codex-plugin/`; repo marketplace evidence; ITO-46 | In progress | Official directory path still pending |
|
| Prepare release notes, articles, tweets, and push notifications | docs/releases/2.0.0-rc.1 social and release-copy files | in_progress | release notes, X thread, and LinkedIn draft are present | URL-backed refresh and publish approval still pending |
|
||||||
| Release notes and push notifications | `release-notes.md`; `x-thread.md`; `linkedin-post.md`; ITO-47/56 | In progress | Live URLs and publish approval missing |
|
| Advance AgentShield enterprise iteration | AgentShield PR evidence plus enterprise roadmap | in_progress | AgentShield enterprise PR evidence is mirrored in the GA roadmap | cross-harness policy integration and richer fleet-review workflow remain pending after fleet-summary consumption and evidence-path surfacing shipped |
|
||||||
| AgentShield enterprise iteration | AgentShield PRs #83-#86; ITO-48/49 | In progress | Live IOC update loop and cross-harness depth pending |
|
| Advance ECC Tools native payments and AI-native harness-agnostic app | ECC Tools PR evidence, billing gate, hosted analysis lanes | in_progress | billing announcement gate, hosted analysis lanes, AgentShield fleet-summary consumption, and hosted finding evidence paths are mirrored in the GA roadmap | live Marketplace test-account readback, hosted promotion telemetry, and richer operator review UX pending |
|
||||||
| ECC Tools native payments announcement | ECC-Tools #75; ITO-50 | In progress | Live Marketplace test-account readback pending |
|
| Audit, prune, or attach legacy work | docs/stale-pr-salvage-ledger.md and legacy inventory | not_complete | legacy salvage ledger and ITO-55 tracking are present | final translation/manual-review tail remains |
|
||||||
| ECC Tools AI-native harness-agnostic roadmap | ITO-51/52/53/54 | In progress | Implementation and hosted deep-analysis proof pending |
|
| Keep Linear roadmap detailed and progress tracking synchronized | Linear project mirror plus progress-sync contract | in_progress | repo mirror and progress-sync contract are present | recurring Linear status sync and productized realtime sync remain pending |
|
||||||
| Linear roadmap extremely detailed | Linear `ECC Platform Roadmap`; ITO-44 through ITO-59 | Current | Keep status comments synchronized |
|
| Provide ECC 2.0 observability for self-use | observability readiness gate | complete | observability:ready command and readiness doc exist | runtime/dashboard implementation can continue after release gates |
|
||||||
| Legacy work audited, pruned, or attached | `docs/legacy-artifact-inventory.md`; ITO-55 | In progress | Final salvage/prune pass pending |
|
| Keep Mini Shai-Hulud/TanStack protection loop current | supply-chain watch plus runbook | current | scheduled supply-chain watch now emits IOC and advisory-source refresh artifacts | Linear status synchronization remains ITO-57 follow-up after each significant merge batch |
|
||||||
| Realtime progress tracking with Linear | ITO-54; Linear progress comments | In progress | Productized sync/observability plane pending |
|
|
||||||
| ECC 2.0 observability | `docs/architecture/observability-readiness.md`; ITO-54 | In progress | Runtime/dashboard implementation pending |
|
|
||||||
|
|
||||||
## Linear Operating State
|
## Top Actions
|
||||||
|
|
||||||
Project:
|
- `ecc-preview-pack`: final clean-checkout release approval and publish evidence still pending
|
||||||
<https://linear.app/itomarkets/project/ecc-platform-roadmap-52b328ee03e1>
|
- `hermes-specialized-skills`: final preview-pack smoke and release review pending
|
||||||
|
- `naming-and-plugin-publication`: real tag/push, marketplace submission, and final channel choice remain approval-gated
|
||||||
Active issue state after this pass:
|
- `release-notes-and-notifications`: URL-backed refresh and publish approval still pending
|
||||||
|
- `agentshield-enterprise-iteration`: cross-harness policy integration and richer fleet-review workflow remain pending after fleet-summary consumption and evidence-path surfacing shipped
|
||||||
| Issue | Lane | State |
|
- `ecc-tools-next-level`: live Marketplace test-account readback, hosted promotion telemetry, and richer operator review UX pending
|
||||||
| --- | --- | --- |
|
- `legacy-salvage`: final translation/manual-review tail remains
|
||||||
| ITO-44 | Completion audit and readiness dashboard | In Progress |
|
- `linear-roadmap-and-progress`: recurring Linear status sync and productized realtime sync remain pending
|
||||||
| ITO-57 | Supply-chain intelligence and local protection loop | In Progress |
|
|
||||||
| ITO-59 | Discussions and public response queue | Current; Linear status sync pending |
|
|
||||||
|
|
||||||
Still-open lanes:
|
|
||||||
|
|
||||||
- ITO-45: ECC 2.0 preview pack, Hermes skills, packaging, and cross-harness
|
|
||||||
readiness.
|
|
||||||
- ITO-46: name availability, Claude plugin, Codex plugin, and package channels.
|
|
||||||
- ITO-47: release notes, articles, and social copy since last release.
|
|
||||||
- ITO-48 and ITO-49: AgentShield enterprise iteration and live supply-chain
|
|
||||||
intelligence.
|
|
||||||
- ITO-50 through ITO-54: ECC Tools payments, deep analysis, setup
|
|
||||||
recommendations, queue automation, Linear sync, and observability.
|
|
||||||
- ITO-55: legacy audit, prune, attach, or salvage.
|
|
||||||
- ITO-56: final publication gate, release notes, and push notifications.
|
|
||||||
- ITO-58: ECC Tools GitHub access blocker.
|
|
||||||
|
|
||||||
## Decisions From This Pass
|
|
||||||
|
|
||||||
- The checked GitHub queues are below the explicit target, so the next work
|
|
||||||
should not spend more time closing nonexistent PRs/issues.
|
|
||||||
- The discussion queue is current and repeatable through `discussion:audit`.
|
|
||||||
ITO-59 remains open only for recurring Linear/status synchronization.
|
|
||||||
- The Mini Shai-Hulud/TanStack protection pass now has a durable scheduled
|
|
||||||
watch workflow. ITO-57 remains open for advisory-source refresh automation
|
|
||||||
and Linear status synchronization.
|
|
||||||
- The release is still blocked by publication, package, plugin, billing, and
|
|
||||||
announcement gates. Passing `platform:audit` alone is not proof that ECC 2.0
|
|
||||||
is publishable.
|
|
||||||
|
|
||||||
## Next Work Order
|
## Next Work Order
|
||||||
|
|
||||||
1. Build the ITO-44 completion dashboard into a repeatable command or generated
|
1. Regenerate this dashboard from the final release commit before publication evidence is recorded.
|
||||||
markdown artifact.
|
2. Continue ITO-57 with Linear status synchronization for the scheduled supply-chain watch advisory-source report.
|
||||||
2. Run `platform:audit` and `discussion:audit` from the final release commit
|
3. Advance ECC Tools live Marketplace test-account readback before publishing native-payments announcement copy.
|
||||||
before recording publication evidence.
|
4. Resume ITO-45, ITO-46, and ITO-56 only after the generated dashboard and final release gates are refreshed.
|
||||||
3. Continue ITO-57 by adding advisory-source refresh automation and Linear
|
|
||||||
status synchronization for the scheduled supply-chain watch.
|
|
||||||
4. Resume release/publication lanes ITO-45, ITO-46, and ITO-56 only after the
|
|
||||||
readiness dashboard can be refreshed from commands.
|
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ surfaces, or posting announcements.
|
|||||||
| `docs/releases/2.0.0-rc.1/quickstart.md` | Clone-to-first-workflow path | Covers clone, install, verify, first skill, and harness switch |
|
| `docs/releases/2.0.0-rc.1/quickstart.md` | Clone-to-first-workflow path | Covers clone, install, verify, first skill, and harness switch |
|
||||||
| `docs/releases/2.0.0-rc.1/launch-checklist.md` | Operator launch checklist | Must remain approval-gated for release, package, plugin, and announcement actions |
|
| `docs/releases/2.0.0-rc.1/launch-checklist.md` | Operator launch checklist | Must remain approval-gated for release, package, plugin, and announcement actions |
|
||||||
| `docs/releases/2.0.0-rc.1/publication-readiness.md` | Release gate | Requires fresh evidence from the exact release commit |
|
| `docs/releases/2.0.0-rc.1/publication-readiness.md` | Release gate | Requires fresh evidence from the exact release commit |
|
||||||
| `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-15.md` | Current May 15 queue, roadmap, security, AgentShield #86 evidence-pack provenance, ECC Tools billing-gate, CI cache, and `ecc2` test evidence through PR #1936 | Must be superseded by a final clean-checkout evidence file before real publication |
|
| `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-15.md` | Current May 15 queue, roadmap, security, supply-chain watch, no-lifecycle CI install hardening, AgentShield #86 evidence-pack provenance, ECC Tools billing-gate, Actions cache purge, and `ecc2` test evidence through PR #1941 | Must be superseded by a final clean-checkout evidence file before real publication |
|
||||||
|
| `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-16.md` | Current May 16 queue cleanup, recsys skill merge, GateGuard triage, PR #1947 supply-chain protection, AgentShield #87 plugin-cache confidence evidence, AgentShield #88 evidence-pack inspect/readback, AgentShield #89 evidence-pack fleet routing, ECC-Tools #76 fleet-summary consumption, ECC-Tools #77 hosted finding evidence paths, dashboard refresh, and combined Node/Rust/release-surface gate evidence through the May 16 mirror | Must still be repeated from a strict clean checkout before real publication |
|
||||||
| `docs/releases/2.0.0-rc.1/naming-and-publication-matrix.md` | Naming, slug, and publication-path decision record | Keeps `Everything Claude Code / ECC`, npm `ecc-universal`, and plugin slug `ecc` for rc.1 |
|
| `docs/releases/2.0.0-rc.1/naming-and-publication-matrix.md` | Naming, slug, and publication-path decision record | Keeps `Everything Claude Code / ECC`, npm `ecc-universal`, and plugin slug `ecc` for rc.1 |
|
||||||
| `docs/releases/2.0.0-rc.1/x-thread.md` | X launch draft | Must replace placeholders with live URLs after release/package/plugin publication |
|
| `docs/releases/2.0.0-rc.1/x-thread.md` | X launch draft | Must replace placeholders with live URLs after release/package/plugin publication |
|
||||||
| `docs/releases/2.0.0-rc.1/linkedin-post.md` | LinkedIn launch draft | Must replace placeholders with live URLs after release/package/plugin publication |
|
| `docs/releases/2.0.0-rc.1/linkedin-post.md` | LinkedIn launch draft | Must replace placeholders with live URLs after release/package/plugin publication |
|
||||||
|
|||||||
@@ -71,18 +71,21 @@ Project documents added in Linear:
|
|||||||
| PR #1933 | Expanded home-scan IOC coverage to Claude `settings.local.json`, `.claude/hooks/hooks.json`, and user-level VS Code / Code Insiders `tasks.json` across macOS, Linux, and Windows |
|
| PR #1933 | Expanded home-scan IOC coverage to Claude `settings.local.json`, `.claude/hooks/hooks.json`, and user-level VS Code / Code Insiders `tasks.json` across macOS, Linux, and Windows |
|
||||||
| PR #1934 | Switched ordinary CI dependency caches to restore-only `actions/cache/restore` usage so test jobs do not save mutable dependency state back into shared caches |
|
| PR #1934 | Switched ordinary CI dependency caches to restore-only `actions/cache/restore` usage so test jobs do not save mutable dependency state back into shared caches |
|
||||||
| PR #1935 | Stabilized `ecc2` current-directory-mutating tests with a test-only serialized current-dir guard, preserving the Rust release-surface gate under parallel test execution |
|
| PR #1935 | Stabilized `ecc2` current-directory-mutating tests with a test-only serialized current-dir guard, preserving the Rust release-surface gate under parallel test execution |
|
||||||
|
| PR #1940 | Added `.github/workflows/supply-chain-watch.yml`, scheduled every 6 hours, so the TanStack/Mini Shai-Hulud/node-ipc IOC scan and npm signature/audit checks produce a durable `supply-chain-ioc-report.json` artifact |
|
||||||
|
| PR #1941 | Removed GitHub Actions dependency cache use from CI test workflows, disabled package-manager lifecycle scripts for npm/pnpm/Yarn/Bun installs, purged existing Actions caches, and added validator tests that reject unsafe install/cache patterns |
|
||||||
| AgentShield PR #83 | Merged Mini Shai-Hulud IOC coverage for TanStack, Mistral, OpenSearch, Guardrails, UiPath, Squawk, Claude Code / VS Code persistence, and dead-man switch artifacts |
|
| AgentShield PR #83 | Merged Mini Shai-Hulud IOC coverage for TanStack, Mistral, OpenSearch, Guardrails, UiPath, Squawk, Claude Code / VS Code persistence, and dead-man switch artifacts |
|
||||||
| AgentShield PR #84 | Merged the broader Mini Shai-Hulud full-campaign affected-package table, including additional `@cap-js`, `@draftlab`, `@tallyui`, `intercom-client`, `lightning`, and related package/version IOCs |
|
| AgentShield PR #84 | Merged the broader Mini Shai-Hulud full-campaign affected-package table, including additional `@cap-js`, `@draftlab`, `@tallyui`, `intercom-client`, `lightning`, and related package/version IOCs |
|
||||||
| AgentShield PR #85 | Added GitHub Action supply-chain verification, gating, and evidence packs so AgentShield's enterprise scanner release path has a verified registry-signature surface |
|
| AgentShield PR #85 | Added GitHub Action supply-chain verification, gating, and evidence packs so AgentShield's enterprise scanner release path has a verified registry-signature surface |
|
||||||
| AgentShield PR #86 | Added `ci-context.json` to AgentShield evidence packs with whitelisted GitHub Actions workflow, commit, run, and runtime provenance while keeping arbitrary environment variables and tokens out of the bundle |
|
| AgentShield PR #86 | Added `ci-context.json` to AgentShield evidence packs with whitelisted GitHub Actions workflow, commit, run, and runtime provenance while keeping arbitrary environment variables and tokens out of the bundle |
|
||||||
| ECC-Tools PR #75 | Tightened the native GitHub payments announcement gate so public billing claims remain blocked until live Marketplace-managed test-account readback is ready |
|
| ECC-Tools PR #75 | Tightened the native GitHub payments announcement gate so public billing claims remain blocked until live Marketplace-managed test-account readback is ready |
|
||||||
| Trunk merge commits | `f04702bdac132662c8496e817bcd850c86e2b854`, `ee85e1482e3d6322ddb2706392ea0fc97469bd26`, `13585f1092c92fa3f20ffe0d756e40c5720b0de5`, `553d507ea63bc252e815a924c0d2baea961351a1`, `c0bac4d6ced7f78a5464c6e3fd8cfbb43515a9d5`, `c2c54e7c0b84a213848b9ab3dfeb3ae16fb9844d`, `6b8a49a6eed11cc7df19d8b1f2add085b37cf466`, `1949d75e18e59a37de269d88b188fc701f5cf122` |
|
| Trunk merge commits | `f04702bdac132662c8496e817bcd850c86e2b854`, `ee85e1482e3d6322ddb2706392ea0fc97469bd26`, `13585f1092c92fa3f20ffe0d756e40c5720b0de5`, `553d507ea63bc252e815a924c0d2baea961351a1`, `c0bac4d6ced7f78a5464c6e3fd8cfbb43515a9d5`, `c2c54e7c0b84a213848b9ab3dfeb3ae16fb9844d`, `6b8a49a6eed11cc7df19d8b1f2add085b37cf466`, `1949d75e18e59a37de269d88b188fc701f5cf122`, `6951b8d5d29d13cac6b89b461104ad03838553de`, `f7035b5644ffc857879b71c39353b2141f17c3f0` |
|
||||||
| AgentShield merge commits | `f899b27ba3fa60ec7e0dca41cc2dadcb1a1fb75d`, `d1aa5313afd915d0b7296e57aabaeb979b1ea93b`, `908d8f3a52a6a65b21e737339b56906603eb1345`, `69a5e25b675b77666d0c96abc22639a5ba883403` |
|
| AgentShield merge commits | `f899b27ba3fa60ec7e0dca41cc2dadcb1a1fb75d`, `d1aa5313afd915d0b7296e57aabaeb979b1ea93b`, `908d8f3a52a6a65b21e737339b56906603eb1345`, `69a5e25b675b77666d0c96abc22639a5ba883403` |
|
||||||
| ECC-Tools merge commits | `6d00d67043e92cadc80f160bfe947115bfef33b1` |
|
| ECC-Tools merge commits | `6d00d67043e92cadc80f160bfe947115bfef33b1` |
|
||||||
| Local IOC tests | `node tests/ci/scan-supply-chain-iocs.test.js` passed 15/15 |
|
| Local IOC tests | `node tests/ci/scan-supply-chain-iocs.test.js` passed 15/15 |
|
||||||
| Unicode safety | `node scripts/ci/check-unicode-safety.js` passed |
|
| Unicode safety | `node scripts/ci/check-unicode-safety.js` passed |
|
||||||
| IOC scan | `node scripts/ci/scan-supply-chain-iocs.js --root <ECC-workspace> --home` passed with 1241 files inspected |
|
| IOC scan | `node scripts/ci/scan-supply-chain-iocs.js --root <ECC-workspace> --home` passed with 229 files inspected after the no-lifecycle install refresh |
|
||||||
| npm registry verification | `npm audit signatures` verified 241 registry signatures and 30 attestations; `npm audit --audit-level=moderate` found 0 vulnerabilities |
|
| npm registry verification | `npm audit signatures` verified 241 registry signatures and 30 attestations; `npm audit --audit-level=high` found 0 vulnerabilities |
|
||||||
|
| Actions cache purge | `gh cache delete --all --succeed-on-no-caches` completed and `gh cache list --limit 20` returned no caches |
|
||||||
| Rust release-surface gate | `cd ecc2 && cargo test` passed 462/462 with the existing 14 dead-code/unused warnings |
|
| Rust release-surface gate | `cd ecc2 && cargo test` passed 462/462 with the existing 14 dead-code/unused warnings |
|
||||||
| Root suite | `node tests/run-all.js` passed 2442/2442, 0 failed |
|
| Root suite | `node tests/run-all.js` passed 2442/2442, 0 failed |
|
||||||
| Repo sweeps | Targeted persistence path checks found no active `gh-token-monitor`, `pgsql-monitor`, `transformers.pyz`, or `pgmonitor.py` artifacts |
|
| Repo sweeps | Targeted persistence path checks found no active `gh-token-monitor`, `pgsql-monitor`, `transformers.pyz`, or `pgmonitor.py` artifacts |
|
||||||
@@ -105,10 +108,12 @@ the extra affected npm package scopes and unscoped packages reported in the
|
|||||||
current Wiz table, rebuilding `dist/action.js` and `dist/index.js`, and passing
|
current Wiz table, rebuilding `dist/action.js` and `dist/index.js`, and passing
|
||||||
1758/1758 local tests plus the full AgentShield GitHub Actions matrix before
|
1758/1758 local tests plus the full AgentShield GitHub Actions matrix before
|
||||||
merge.
|
merge.
|
||||||
AgentShield PR #85 and trunk PR #1934 extend the response from IOC detection
|
AgentShield PR #85 and trunk PRs #1934, #1940, and #1941 extend the response
|
||||||
into release-path hardening: AgentShield now records registry-signature evidence
|
from IOC detection into release-path hardening: AgentShield now records
|
||||||
for its action surface, while trunk CI restore-only dependency caches avoid
|
registry-signature evidence for its action surface, trunk has a scheduled IOC
|
||||||
writing ordinary test dependency state back into shared caches.
|
watch workflow, and trunk CI no longer uses dependency caches or package-manager
|
||||||
|
lifecycle scripts in the test install matrix during active supply-chain
|
||||||
|
hardening.
|
||||||
AgentShield PR #86 completes the next evidence-pack provenance slice:
|
AgentShield PR #86 completes the next evidence-pack provenance slice:
|
||||||
`agentshield scan --evidence-pack <dir>` now writes `ci-context.json`, includes
|
`agentshield scan --evidence-pack <dir>` now writes `ci-context.json`, includes
|
||||||
that artifact in the signed bundle digest, documents it in the bundle README,
|
that artifact in the signed bundle digest, documents it in the bundle README,
|
||||||
|
|||||||
82
docs/releases/2.0.0-rc.1/publication-evidence-2026-05-16.md
Normal file
82
docs/releases/2.0.0-rc.1/publication-evidence-2026-05-16.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# ECC v2.0.0-rc.1 Publication Evidence - 2026-05-16
|
||||||
|
|
||||||
|
This is release-readiness evidence only. It does not create a GitHub release,
|
||||||
|
npm publication, plugin tag, marketplace submission, or announcement post.
|
||||||
|
|
||||||
|
## Source Commit
|
||||||
|
|
||||||
|
| Field | Evidence |
|
||||||
|
| --- | --- |
|
||||||
|
| Upstream main | `6bced468d76b269243a6f0bd28472853aa78e0e4` |
|
||||||
|
| Git remote | `https://github.com/affaan-m/everything-claude-code.git` |
|
||||||
|
| Evidence scope | Current `main` after PR #1944, PR #1945, issue #1946 triage, PR #1947 supply-chain protection, AgentShield PR #87, AgentShield PR #88, AgentShield PR #89, ECC-Tools PR #76, ECC-Tools PR #77, ITO-57 sync, and operator dashboard refresh |
|
||||||
|
| Local status caveat | `git status --short --branch` showed `## main...origin/main` plus unrelated untracked `docs/drafts/` |
|
||||||
|
|
||||||
|
The actual release operator should repeat all publish-facing checks from the
|
||||||
|
final release commit with a strictly clean checkout before publishing.
|
||||||
|
|
||||||
|
## Queue And Discussion State
|
||||||
|
|
||||||
|
| Surface | Command | Result |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Trunk PRs | `gh pr list --state open --json number,title,url --limit 20` | `[]` |
|
||||||
|
| Trunk issues | `gh issue list --state open --json number,title,url --limit 20` | `[]` |
|
||||||
|
| Platform audit | `node scripts/platform-audit.js --json --allow-untracked docs/drafts/` | Ready; open PRs 0, open issues 0, discussion maintainer-touch gaps 0, discussion missing-answer gaps 0, blocking dirty files 0 |
|
||||||
|
| Operator dashboard | `npm run operator:dashboard -- --json --allow-untracked docs/drafts/` | `dashboardReady: true`, `platformReady: true`, head `6bced468d76b269243a6f0bd28472853aa78e0e4` |
|
||||||
|
|
||||||
|
## Merge And Triage Batch
|
||||||
|
|
||||||
|
| Item | Result |
|
||||||
|
| --- | --- |
|
||||||
|
| PR #1944 | Merged statusline ANSI palette update as `50ac061f9e72d7daa137f1bd08760cf74e9b577d`; targeted `node tests/hooks/ecc-statusline.test.js` and `node scripts/ci/validate-hooks.js` passed before merge |
|
||||||
|
| PR #1945 | Merged `recsys-pipeline-architect` community skill as `9e973b29fb1a2a0aeb9e6980017b67c3ddb05201`; maintainer patches synced catalog counts and removed emoji blocked by Unicode safety |
|
||||||
|
| Issue #1946 | Closed as triaged with a corrected maintainer comment; Linear `ITO-60` now tracks GateGuard proactive fact-forcing preflight UX |
|
||||||
|
| PR #1947 | Merged scheduled supply-chain watch/advisory-source evidence as `4093d1bb7a14db1b4d4ea5bd00f2073baf94bfb0`; trunk now has the TanStack/Mini Shai-Hulud/node-ipc IOC scan plus advisory-source report surfaces wired into scheduled watch evidence |
|
||||||
|
| AgentShield PR #87 | Merged plugin-cache runtime-confidence classification as `26bb44650663816d07180e0d20c1895e431a326c`; installed Claude plugin cache findings now emit `runtimeConfidence: plugin-cache`, `plugins/cache` only maps to Claude cache under `.claude`, and cached hook implementations are no longer mislabeled as active `hook-code` |
|
||||||
|
| AgentShield PR #88 | Merged evidence-pack inspect/readback as `65ed6e2a87545dc99d962b58413f49096a4d70ec`; `agentshield evidence-pack inspect` now emits verified JSON/text summaries for report, policy, baseline, supply-chain, CI context, remediation, and malformed artifact errors |
|
||||||
|
| AgentShield PR #89 | Merged evidence-pack fleet routing as `521ada9091bb6d818511ab8589ae675b920c106a`; `agentshield evidence-pack fleet <dirs...> [--json]` now aggregates multiple verified bundles into ready, security-blocker, policy-review, baseline-regression, supply-chain-review, and invalid routes with finding, policy, baseline, supply-chain, and remediation totals |
|
||||||
|
| ECC-Tools PR #76 | Merged AgentShield fleet-summary consumption as `5bde2328d15f584481fb6334e6960716dbf3e16f`; hosted `security-evidence-review` now recognizes `agentshield-evidence/fleet-summary.json`, classifies it as `evidence-pack-fleet`, routes invalid/security-blocker/policy/baseline/supply-chain fleet outcomes into hosted findings, and fails closed on malformed fleet JSON |
|
||||||
|
| ECC-Tools PR #77 | Merged hosted finding source-evidence output as `31fd883b3f0cee135aee4839b01d34855b7867f6`; hosted job PR comments and check-run details now include an `Evidence` column with up to three source evidence paths per finding, including AgentShield fleet-derived findings |
|
||||||
|
| ITO-57 | Updated with PR #1947 advisory-source evidence, post-merge source refresh, IOC scan, npm audit/signature checks, and OpenAI app update caveat |
|
||||||
|
| ITO-49 | Updated with AgentShield PR #87, #88, and #89 merge evidence, local test evidence, CI status, live `~/.claude` scan classification counts, and local Mini Shai-Hulud protection scan results |
|
||||||
|
| ITO-50 | Updated with ECC-Tools PR #76 and PR #77 merge evidence, hosted security review behavior, hosted finding evidence-path behavior, local test evidence, and remote Verify/Security Audit/Workers build checks |
|
||||||
|
| ITO-44 | Updated with queue cleanup, dashboard refresh, and remaining macro gaps |
|
||||||
|
|
||||||
|
## Release Gate Commands
|
||||||
|
|
||||||
|
| Gate | Command | Result |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Root suite | `npm test` | 2469 passed, 0 failed |
|
||||||
|
| Rust `ecc2` suite | `cd ecc2 && cargo test` | 462 passed, 0 failed; existing dead-code/unused warnings only |
|
||||||
|
| Release surface | `node tests/docs/ecc2-release-surface.test.js` | 20 passed |
|
||||||
|
| Harness adapters | `npm run harness:adapters -- --check` | PASS; 11 adapters |
|
||||||
|
| Harness audit | `npm run harness:audit -- --format json` | 70/70, no top actions |
|
||||||
|
| Observability readiness | `npm run observability:ready` | 21/21, ready yes |
|
||||||
|
| Supply-chain IOC scan | `npm run security:ioc-scan` | Passed; 227 files inspected |
|
||||||
|
| Advisory source refresh | `npm run security:advisory-sources -- --refresh --json` | Ready; 9 active sources; Linear payload still points at `ITO-57` for sync |
|
||||||
|
| npm audit | `npm audit --audit-level=moderate` | 0 vulnerabilities |
|
||||||
|
| npm signatures | `npm audit signatures` | 241 verified registry signatures; 30 verified attestations |
|
||||||
|
| Dashboard renderer | `node tests/scripts/operator-readiness-dashboard.test.js` | 7 passed, 0 failed |
|
||||||
|
|
||||||
|
## Current Publication Blockers
|
||||||
|
|
||||||
|
- GitHub prerelease `v2.0.0-rc.1` is still not created in this pass.
|
||||||
|
- npm `ecc-universal@2.0.0-rc.1` is still not published to the `next` dist-tag.
|
||||||
|
- Claude plugin tag and marketplace propagation remain approval-gated.
|
||||||
|
- Codex repo-marketplace distribution is verified for rc.1, but official
|
||||||
|
Plugin Directory publishing remains blocked on OpenAI's coming-soon
|
||||||
|
self-serve publishing surface.
|
||||||
|
- ECC Tools billing/native-payments copy remains blocked until live
|
||||||
|
Marketplace-managed test-account readback returns an announcement-ready gate.
|
||||||
|
- Release notes, X, LinkedIn, GitHub release, and longform copy still need final
|
||||||
|
live URLs after release/package/plugin URLs exist.
|
||||||
|
- The local checkout still has unrelated untracked `docs/drafts/`, so a strict
|
||||||
|
clean-checkout release pass remains required before real publication.
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
The public PR queue, issue queue, and discussion queue are clear, and the rc.1
|
||||||
|
preview pack passed the main Node, Rust, release-surface, harness, observability,
|
||||||
|
and supply-chain gates on May 16, 2026. This improves publication readiness but
|
||||||
|
does not replace the approval-gated release, package, plugin, and announcement
|
||||||
|
steps in `publication-readiness.md`.
|
||||||
@@ -15,12 +15,20 @@ For the May 13 release-readiness evidence refresh, see
|
|||||||
For the May 13 post-hardening evidence refresh after PR #1850 and PR #1851, see
|
For the May 13 post-hardening evidence refresh after PR #1850 and PR #1851, see
|
||||||
[`publication-evidence-2026-05-13-post-hardening.md`](publication-evidence-2026-05-13-post-hardening.md).
|
[`publication-evidence-2026-05-13-post-hardening.md`](publication-evidence-2026-05-13-post-hardening.md).
|
||||||
For the May 15 queue, discussion, Linear roadmap, Mini Shai-Hulud/TanStack
|
For the May 15 queue, discussion, Linear roadmap, Mini Shai-Hulud/TanStack
|
||||||
follow-up, restore-only cache, AgentShield release-verification, billing-gate,
|
follow-up, scheduled supply-chain watch, no-lifecycle CI install hardening,
|
||||||
|
GitHub Actions cache purge, AgentShield release-verification, billing-gate,
|
||||||
AgentShield #86 evidence-pack provenance, and `ecc2` current-dir guard evidence
|
AgentShield #86 evidence-pack provenance, and `ecc2` current-dir guard evidence
|
||||||
refresh through PR #1936, see
|
refresh through PR #1941, see
|
||||||
[`publication-evidence-2026-05-15.md`](publication-evidence-2026-05-15.md).
|
[`publication-evidence-2026-05-15.md`](publication-evidence-2026-05-15.md).
|
||||||
|
For the May 16 queue cleanup, recsys skill merge, GateGuard issue triage,
|
||||||
|
AgentShield #87 plugin-cache runtime-confidence evidence, AgentShield #88
|
||||||
|
evidence-pack inspect/readback, AgentShield #89 evidence-pack fleet routing,
|
||||||
|
ECC-Tools #76 fleet-summary consumption, ECC-Tools #77 hosted finding evidence
|
||||||
|
paths, operator dashboard refresh, and combined final-gate rerun on current
|
||||||
|
`main`, see
|
||||||
|
[`publication-evidence-2026-05-16.md`](publication-evidence-2026-05-16.md).
|
||||||
For the operator-facing prompt-to-artifact readiness dashboard from the same
|
For the operator-facing prompt-to-artifact readiness dashboard from the same
|
||||||
May 15 pass, see
|
May 16 pass, see
|
||||||
[`operator-readiness-dashboard-2026-05-15.md`](operator-readiness-dashboard-2026-05-15.md).
|
[`operator-readiness-dashboard-2026-05-15.md`](operator-readiness-dashboard-2026-05-15.md).
|
||||||
|
|
||||||
## Release Identity Matrix
|
## Release Identity Matrix
|
||||||
@@ -60,21 +68,21 @@ Record the exact commit SHA and command output before any publication action:
|
|||||||
|
|
||||||
| Evidence | Command | Required result | Recorded output |
|
| Evidence | Command | Required result | Recorded output |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| Clean release branch | `git status --short --branch` | On intended release commit; no unrelated files | Pending final clean-checkout release pass; May 13 evidence branch still had unrelated untracked `docs/drafts/` |
|
| Clean release branch | `git status --short --branch` | On intended release commit; no unrelated files | Pending final strict clean-checkout release pass; `publication-evidence-2026-05-16.md` records current `main` with unrelated untracked `docs/drafts/` |
|
||||||
| Harness audit | `npm run harness:audit -- --format json` | 70/70 passing | `publication-evidence-2026-05-13.md`: 70/70 |
|
| Harness audit | `npm run harness:audit -- --format json` | 70/70 passing | `publication-evidence-2026-05-16.md`: 70/70 |
|
||||||
| Adapter scorecard | `npm run harness:adapters -- --check` | PASS | `publication-evidence-2026-05-13.md`: PASS, 11 adapters |
|
| Adapter scorecard | `npm run harness:adapters -- --check` | PASS | `publication-evidence-2026-05-16.md`: PASS, 11 adapters |
|
||||||
| Observability readiness | `npm run observability:ready` | 21/21 passing | `publication-evidence-2026-05-13-post-hardening.md`: 21/21, ready true after release-safety gate refresh |
|
| Observability readiness | `npm run observability:ready` | 21/21 passing | `publication-evidence-2026-05-16.md`: 21/21, ready yes |
|
||||||
| Release safety gate | `npm run observability:ready -- --format json` | Release Safety category passing with publication readiness, supply-chain, workflow security, package surface, and release-surface evidence | `publication-evidence-2026-05-13-post-hardening.md`: Release Safety 3/3 |
|
| Release safety gate | `npm run observability:ready -- --format json` | Release Safety category passing with publication readiness, supply-chain, workflow security, package surface, and release-surface evidence | `publication-evidence-2026-05-13-post-hardening.md`: Release Safety 3/3 |
|
||||||
| Supply-chain verification | `npm audit --json`; `npm audit signatures`; `cd ecc2 && cargo audit -q`; Dependabot alerts; GitGuardian Security Checks | 0 vulnerabilities/alerts, registry signatures verified, GitGuardian clean | `publication-evidence-2026-05-15.md`: npm registry signatures and attestations verified, 0 moderate-or-higher npm vulnerabilities, Mini Shai-Hulud/TanStack IOC scan clean |
|
| Supply-chain verification | `npm audit --json`; `npm audit signatures`; `cd ecc2 && cargo audit -q`; Dependabot alerts; GitGuardian Security Checks | 0 vulnerabilities/alerts, registry signatures verified, GitGuardian clean | `publication-evidence-2026-05-16.md`: npm registry signatures and attestations verified, 0 moderate-or-higher npm vulnerabilities, supply-chain IOC scan clean |
|
||||||
| Root suite | `node tests/run-all.js` | 0 failures | `publication-evidence-2026-05-15.md`: 2442 passed, 0 failed |
|
| Root suite | `node tests/run-all.js` | 0 failures | `publication-evidence-2026-05-16.md`: `npm test` passed 2469/2469, 0 failed |
|
||||||
| Markdown lint | `npx markdownlint-cli '**/*.md' --ignore node_modules` | 0 failures | `publication-evidence-2026-05-13.md`: passed after zh-CN CLAUDE list-marker normalization |
|
| Markdown lint | `npx markdownlint-cli '**/*.md' --ignore node_modules` | 0 failures | `publication-evidence-2026-05-13.md`: passed after zh-CN CLAUDE list-marker normalization |
|
||||||
| Package surface | `node tests/scripts/npm-publish-surface.test.js` | 0 failures; no Python bytecode in npm tarball | `2/2` passed in May 12 evidence pass |
|
| Package surface | `node tests/scripts/npm-publish-surface.test.js` | 0 failures; no Python bytecode in npm tarball | `2/2` passed in May 12 evidence pass |
|
||||||
| Release surface | `node tests/docs/ecc2-release-surface.test.js` | 0 failures | `publication-evidence-2026-05-13.md`: 18/18 passed |
|
| Release surface | `node tests/docs/ecc2-release-surface.test.js` | 0 failures | `publication-evidence-2026-05-16.md`: 20/20 passed |
|
||||||
| Optional Rust surface | `cd ecc2 && cargo test` | 0 failures or explicit deferral | `publication-evidence-2026-05-15.md`: 462/462 passed, existing warnings only after PR #1935 current-dir guard |
|
| Optional Rust surface | `cd ecc2 && cargo test` | 0 failures or explicit deferral | `publication-evidence-2026-05-16.md`: 462/462 passed, existing warnings only |
|
||||||
| Queue baseline | `gh pr list` / `gh issue list` across trunk, AgentShield, JARVIS, ECC Tools, and ECC website | Under 20 open PRs and under 20 open issues | `publication-evidence-2026-05-15.md`: platform audit ready, 0 open PRs and 0 open issues across checked repos |
|
| Queue baseline | `gh pr list` / `gh issue list` across trunk, AgentShield, JARVIS, ECC Tools, and ECC website | Under 20 open PRs and under 20 open issues | `publication-evidence-2026-05-16.md`: platform audit ready, 0 open PRs and 0 open issues across checked repos |
|
||||||
| Discussion baseline | `node scripts/discussion-audit.js --json` | No unmanaged active discussion queue and no answerable Q&A missing an accepted answer | `publication-evidence-2026-05-15.md`: 58 trunk discussions, 0 without maintainer touch; other tracked repos disabled or 0 |
|
| Discussion baseline | `node scripts/discussion-audit.js --json` | No unmanaged active discussion queue and no answerable Q&A missing an accepted answer | `publication-evidence-2026-05-15.md`: 58 trunk discussions, 0 without maintainer touch; other tracked repos disabled or 0 |
|
||||||
| Linear roadmap | Linear project and issue readback | Detailed roadmap exists with release, security, AgentShield, ECC Tools, legacy, and observability lanes | `publication-evidence-2026-05-15.md`: project and 16 issue lanes recorded |
|
| Linear roadmap | Linear project and issue readback | Detailed roadmap exists with release, security, AgentShield, ECC Tools, legacy, and observability lanes | `publication-evidence-2026-05-15.md`: project and 16 issue lanes recorded |
|
||||||
| Operator readiness dashboard | `node scripts/platform-audit.js --json --allow-untracked docs/drafts/` plus `node scripts/discussion-audit.js --json` | Current queue state mapped to macro-goal deliverables and incomplete gaps | `operator-readiness-dashboard-2026-05-15.md`: live status, command evidence, Linear state, and next work order |
|
| Operator readiness dashboard | `npm run operator:dashboard -- --allow-untracked docs/drafts/ --write docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-15.md` | Current queue state mapped to macro-goal deliverables and incomplete gaps | `operator-readiness-dashboard-2026-05-15.md`: regenerated from `6c8e909d`, 0 open PRs, 0 open issues, 0 discussion gaps |
|
||||||
|
|
||||||
## Do Not Publish If
|
## Do Not Publish If
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ node scripts/ci/scan-supply-chain-iocs.js --home
|
|||||||
npm ci --ignore-scripts
|
npm ci --ignore-scripts
|
||||||
npm audit signatures
|
npm audit signatures
|
||||||
npm audit --audit-level=high
|
npm audit --audit-level=high
|
||||||
|
node scripts/ci/supply-chain-advisory-sources.js --json
|
||||||
node scripts/ci/validate-workflow-security.js
|
node scripts/ci/validate-workflow-security.js
|
||||||
node tests/scripts/npm-publish-surface.test.js
|
node tests/scripts/npm-publish-surface.test.js
|
||||||
node tests/run-all.js
|
node tests/run-all.js
|
||||||
@@ -86,8 +87,10 @@ evidence but do not rotate credentials for a docs-only reference.
|
|||||||
ECC also runs `.github/workflows/supply-chain-watch.yml` every six hours and on
|
ECC also runs `.github/workflows/supply-chain-watch.yml` every six hours and on
|
||||||
manual dispatch. The workflow is read-only, disables checkout credential
|
manual dispatch. The workflow is read-only, disables checkout credential
|
||||||
persistence, installs with `npm ci --ignore-scripts`, verifies npm registry
|
persistence, installs with `npm ci --ignore-scripts`, verifies npm registry
|
||||||
signatures, runs the IOC scanner fixtures, emits
|
signatures, runs the IOC scanner fixtures, runs
|
||||||
`supply-chain-ioc-report.json`, and re-validates GitHub Actions hardening rules.
|
`scripts/ci/supply-chain-advisory-sources.js --refresh --json`, emits
|
||||||
|
`supply-chain-ioc-report.json` and `supply-chain-advisory-sources.json`, and
|
||||||
|
re-validates GitHub Actions hardening rules.
|
||||||
|
|
||||||
Treat a failed scheduled watch as a release blocker until an operator confirms
|
Treat a failed scheduled watch as a release blocker until an operator confirms
|
||||||
whether the failure is a newly reported advisory, a stale scanner fixture, a
|
whether the failure is a newly reported advisory, a stale scanner fixture, a
|
||||||
@@ -96,6 +99,12 @@ needs new indicators, update `scripts/ci/scan-supply-chain-iocs.js`, add fixture
|
|||||||
coverage in `tests/ci/scan-supply-chain-iocs.test.js`, refresh this runbook, and
|
coverage in `tests/ci/scan-supply-chain-iocs.test.js`, refresh this runbook, and
|
||||||
attach the latest JSON artifact to the release evidence.
|
attach the latest JSON artifact to the release evidence.
|
||||||
|
|
||||||
|
The advisory-source artifact is the ITO-57 status payload. It records the
|
||||||
|
trusted source registry, live URL refresh warnings, and a Linear-ready summary.
|
||||||
|
Refresh source coverage through `npm run security:advisory-sources -- --json`
|
||||||
|
before changing IOC coverage, and attach the artifact to the next Linear project
|
||||||
|
status update after each significant merge batch.
|
||||||
|
|
||||||
## Immediate Response
|
## Immediate Response
|
||||||
|
|
||||||
If ECC or a maintainer machine installed a known-bad package version:
|
If ECC or a maintainer machine installed a known-bad package version:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Everything Claude Code (ECC) — 智能体指令
|
# Everything Claude Code (ECC) — 智能体指令
|
||||||
|
|
||||||
这是一个**生产就绪的 AI 编码插件**,提供 60 个专业代理、229 项技能、75 条命令以及自动化钩子工作流,用于软件开发。
|
这是一个**生产就绪的 AI 编码插件**,提供 60 个专业代理、230 项技能、75 条命令以及自动化钩子工作流,用于软件开发。
|
||||||
|
|
||||||
**版本:** 2.0.0-rc.1
|
**版本:** 2.0.0-rc.1
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
agents/ — 60 个专业子代理
|
agents/ — 60 个专业子代理
|
||||||
skills/ — 229 个工作流技能和领域知识
|
skills/ — 230 个工作流技能和领域知识
|
||||||
commands/ — 75 个斜杠命令
|
commands/ — 75 个斜杠命令
|
||||||
hooks/ — 基于触发的自动化
|
hooks/ — 基于触发的自动化
|
||||||
rules/ — 始终遵循的指导方针(通用 + 每种语言)
|
rules/ — 始终遵循的指导方针(通用 + 每种语言)
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
|
|||||||
/plugin list ecc@ecc
|
/plugin list ecc@ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
**搞定!** 你现在可以使用 60 个智能体、229 项技能和 75 个命令了。
|
**搞定!** 你现在可以使用 60 个智能体、230 项技能和 75 个命令了。
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
@@ -1138,7 +1138,7 @@ opencode
|
|||||||
|---------|-------------|----------|--------|
|
|---------|-------------|----------|--------|
|
||||||
| 智能体 | PASS: 60 个 | PASS: 12 个 | **Claude Code 领先** |
|
| 智能体 | PASS: 60 个 | PASS: 12 个 | **Claude Code 领先** |
|
||||||
| 命令 | PASS: 75 个 | PASS: 35 个 | **Claude Code 领先** |
|
| 命令 | PASS: 75 个 | PASS: 35 个 | **Claude Code 领先** |
|
||||||
| 技能 | PASS: 229 项 | PASS: 37 项 | **Claude Code 领先** |
|
| 技能 | PASS: 230 项 | PASS: 37 项 | **Claude Code 领先** |
|
||||||
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
|
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
|
||||||
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
|
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
|
||||||
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
|
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
|
||||||
@@ -1246,7 +1246,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
|
|||||||
|---------|------------|------------|-----------|----------|
|
|---------|------------|------------|-----------|----------|
|
||||||
| **智能体** | 60 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
| **智能体** | 60 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
||||||
| **命令** | 75 | 共享 | 基于指令 | 35 |
|
| **命令** | 75 | 共享 | 基于指令 | 35 |
|
||||||
| **技能** | 229 | 共享 | 10 (原生格式) | 37 |
|
| **技能** | 230 | 共享 | 10 (原生格式) | 37 |
|
||||||
| **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 |
|
| **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 |
|
||||||
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 |
|
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 |
|
||||||
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
|
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
"schemas/",
|
"schemas/",
|
||||||
"scripts/catalog.js",
|
"scripts/catalog.js",
|
||||||
"scripts/ci/scan-supply-chain-iocs.js",
|
"scripts/ci/scan-supply-chain-iocs.js",
|
||||||
|
"scripts/ci/supply-chain-advisory-sources.js",
|
||||||
"scripts/consult.js",
|
"scripts/consult.js",
|
||||||
"scripts/auto-update.js",
|
"scripts/auto-update.js",
|
||||||
"scripts/claw.js",
|
"scripts/claw.js",
|
||||||
@@ -76,6 +77,7 @@
|
|||||||
"scripts/harness-adapter-compliance.js",
|
"scripts/harness-adapter-compliance.js",
|
||||||
"scripts/harness-audit.js",
|
"scripts/harness-audit.js",
|
||||||
"scripts/observability-readiness.js",
|
"scripts/observability-readiness.js",
|
||||||
|
"scripts/operator-readiness-dashboard.js",
|
||||||
"scripts/platform-audit.js",
|
"scripts/platform-audit.js",
|
||||||
"scripts/hooks/",
|
"scripts/hooks/",
|
||||||
"scripts/install-apply.js",
|
"scripts/install-apply.js",
|
||||||
@@ -296,9 +298,11 @@
|
|||||||
"harness:adapters": "node scripts/harness-adapter-compliance.js",
|
"harness:adapters": "node scripts/harness-adapter-compliance.js",
|
||||||
"harness:audit": "node scripts/harness-audit.js",
|
"harness:audit": "node scripts/harness-audit.js",
|
||||||
"observability:ready": "node scripts/observability-readiness.js",
|
"observability:ready": "node scripts/observability-readiness.js",
|
||||||
|
"operator:dashboard": "node scripts/operator-readiness-dashboard.js",
|
||||||
"platform:audit": "node scripts/platform-audit.js",
|
"platform:audit": "node scripts/platform-audit.js",
|
||||||
"discussion:audit": "node scripts/discussion-audit.js",
|
"discussion:audit": "node scripts/discussion-audit.js",
|
||||||
"security:ioc-scan": "node scripts/ci/scan-supply-chain-iocs.js",
|
"security:ioc-scan": "node scripts/ci/scan-supply-chain-iocs.js",
|
||||||
|
"security:advisory-sources": "node scripts/ci/supply-chain-advisory-sources.js",
|
||||||
"claw": "node scripts/claw.js",
|
"claw": "node scripts/claw.js",
|
||||||
"orchestrate:status": "node scripts/orchestration-status.js",
|
"orchestrate:status": "node scripts/orchestration-status.js",
|
||||||
"orchestrate:worker": "bash scripts/orchestrate-codex-worker.sh",
|
"orchestrate:worker": "bash scripts/orchestrate-codex-worker.sh",
|
||||||
|
|||||||
469
scripts/ci/supply-chain-advisory-sources.js
Normal file
469
scripts/ci/supply-chain-advisory-sources.js
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Build a refreshable source report for active supply-chain advisories.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const http = require('http');
|
||||||
|
const https = require('https');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const DEFAULT_GENERATED_AT = () => new Date().toISOString();
|
||||||
|
const DEFAULT_TIMEOUT_MS = 5000;
|
||||||
|
const MAX_REDIRECTS = 5;
|
||||||
|
|
||||||
|
const DEFAULT_ADVISORY_SOURCES = [
|
||||||
|
{
|
||||||
|
id: 'tanstack-postmortem',
|
||||||
|
title: 'TanStack npm supply-chain compromise postmortem',
|
||||||
|
publisher: 'TanStack',
|
||||||
|
url: 'https://tanstack.com/blog/npm-supply-chain-compromise-postmortem',
|
||||||
|
sourceType: 'primary-incident-postmortem',
|
||||||
|
ecosystems: ['npm', 'GitHub Actions'],
|
||||||
|
signals: ['tanstack', 'trusted-publishing-limits', 'github-actions-cache-poisoning'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'github-ghsa-g7cv-rxg3-hmpx',
|
||||||
|
title: 'GitHub Advisory GHSA-g7cv-rxg3-hmpx / CVE-2026-45321',
|
||||||
|
publisher: 'GitHub Advisory Database',
|
||||||
|
url: 'https://github.com/advisories/GHSA-g7cv-rxg3-hmpx',
|
||||||
|
sourceType: 'security-advisory',
|
||||||
|
ecosystems: ['npm', 'AI developer tooling'],
|
||||||
|
signals: ['credential-theft', 'malicious-lifecycle-script', 'tanstack'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tanstack-followup',
|
||||||
|
title: 'TanStack incident follow-up',
|
||||||
|
publisher: 'TanStack',
|
||||||
|
url: 'https://tanstack.com/blog/incident-followup',
|
||||||
|
sourceType: 'primary-incident-followup',
|
||||||
|
ecosystems: ['npm', 'GitHub Actions'],
|
||||||
|
signals: ['remediation', 'trusted-publishing-limits'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'stepsecurity-mini-shai-hulud',
|
||||||
|
title: 'Mini Shai-Hulud campaign analysis',
|
||||||
|
publisher: 'StepSecurity',
|
||||||
|
url: 'https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-hits-the-npm-ecosystem',
|
||||||
|
sourceType: 'incident-analysis',
|
||||||
|
ecosystems: ['npm', 'PyPI', 'AI developer tooling'],
|
||||||
|
signals: ['mini-shai-hulud', 'claude-code-persistence', 'vscode-persistence', 'os-persistence'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'openai-tanstack-response',
|
||||||
|
title: 'OpenAI response to the TanStack npm supply-chain attack',
|
||||||
|
publisher: 'OpenAI',
|
||||||
|
url: 'https://openai.com/index/our-response-to-the-tanstack-npm-supply-chain-attack/',
|
||||||
|
sourceType: 'vendor-response',
|
||||||
|
ecosystems: ['npm', 'AI developer tooling'],
|
||||||
|
signals: ['codex-update', 'developer-tooling-exposure', 'remediation'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'wiz-mini-shai-hulud',
|
||||||
|
title: 'Mini Shai-Hulud broader npm campaign coverage',
|
||||||
|
publisher: 'Wiz',
|
||||||
|
url: 'https://www.wiz.io/blog/mini-shai-hulud-strikes-again-tanstack-more-npm-packages-compromised',
|
||||||
|
sourceType: 'incident-analysis',
|
||||||
|
ecosystems: ['npm', 'PyPI', 'AI developer tooling'],
|
||||||
|
signals: ['mini-shai-hulud', 'opensearch', 'mistral-ai', 'uipath', 'squawk'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'socket-node-ipc',
|
||||||
|
title: 'node-ipc package compromise',
|
||||||
|
publisher: 'Socket',
|
||||||
|
url: 'https://socket.dev/blog/node-ipc-package-compromised',
|
||||||
|
sourceType: 'incident-analysis',
|
||||||
|
ecosystems: ['npm'],
|
||||||
|
signals: ['node-ipc', 'payload-hash', 'destructive-package-behavior'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'npm-trusted-publishers',
|
||||||
|
title: 'npm trusted publishing documentation',
|
||||||
|
publisher: 'npm',
|
||||||
|
url: 'https://docs.npmjs.com/trusted-publishers/',
|
||||||
|
sourceType: 'registry-control-reference',
|
||||||
|
ecosystems: ['npm', 'GitHub Actions'],
|
||||||
|
signals: ['trusted-publishing-limits', 'provenance'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'cisa-npm-compromise',
|
||||||
|
title: 'CISA widespread supply-chain compromise impacting npm ecosystem',
|
||||||
|
publisher: 'CISA',
|
||||||
|
url: 'https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem',
|
||||||
|
sourceType: 'government-alert',
|
||||||
|
ecosystems: ['npm'],
|
||||||
|
signals: ['incident-response', 'credential-rotation', 'npm-compromise'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function normalizeArray(values) {
|
||||||
|
return Array.isArray(values) ? values.filter(Boolean) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCheck(id, status, summary, fix) {
|
||||||
|
return { id, status, summary, fix };
|
||||||
|
}
|
||||||
|
|
||||||
|
function uniqueValues(sources, field) {
|
||||||
|
return new Set(sources.flatMap(source => normalizeArray(source[field])));
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSources(sources) {
|
||||||
|
const checks = [];
|
||||||
|
const ids = new Set();
|
||||||
|
const duplicateIds = [];
|
||||||
|
const invalidSources = [];
|
||||||
|
|
||||||
|
for (const source of sources) {
|
||||||
|
if (ids.has(source.id)) duplicateIds.push(source.id);
|
||||||
|
ids.add(source.id);
|
||||||
|
if (!source.id || !source.title || !source.publisher || !source.url) {
|
||||||
|
invalidSources.push(source.id || '(missing id)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checks.push(createCheck(
|
||||||
|
'advisory-source-count',
|
||||||
|
sources.length >= 8 ? 'pass' : 'fail',
|
||||||
|
`${sources.length} advisory sources registered`,
|
||||||
|
'Track at least eight sources spanning primary advisories, vendor responses, and registry controls.',
|
||||||
|
));
|
||||||
|
|
||||||
|
checks.push(createCheck(
|
||||||
|
'advisory-source-shape',
|
||||||
|
invalidSources.length === 0 && duplicateIds.length === 0 ? 'pass' : 'fail',
|
||||||
|
invalidSources.length === 0 && duplicateIds.length === 0
|
||||||
|
? 'all sources include id, title, publisher, and URL'
|
||||||
|
: `invalid sources: ${[...invalidSources, ...duplicateIds].join(', ')}`,
|
||||||
|
'Fix duplicate or incomplete advisory source records before relying on the watch artifact.',
|
||||||
|
));
|
||||||
|
|
||||||
|
const ecosystems = uniqueValues(sources, 'ecosystems');
|
||||||
|
const requiredEcosystems = ['npm', 'PyPI', 'AI developer tooling'];
|
||||||
|
const missingEcosystems = requiredEcosystems.filter(ecosystem => !ecosystems.has(ecosystem));
|
||||||
|
checks.push(createCheck(
|
||||||
|
'advisory-ecosystem-coverage',
|
||||||
|
missingEcosystems.length === 0 ? 'pass' : 'fail',
|
||||||
|
missingEcosystems.length === 0
|
||||||
|
? 'sources cover npm, PyPI, and AI developer tooling'
|
||||||
|
: `missing ecosystem coverage: ${missingEcosystems.join(', ')}`,
|
||||||
|
'Add sources for every active ecosystem touched by the campaign.',
|
||||||
|
));
|
||||||
|
|
||||||
|
const signals = uniqueValues(sources, 'signals');
|
||||||
|
const requiredSignals = [
|
||||||
|
'tanstack',
|
||||||
|
'mini-shai-hulud',
|
||||||
|
'claude-code-persistence',
|
||||||
|
'vscode-persistence',
|
||||||
|
'os-persistence',
|
||||||
|
'node-ipc',
|
||||||
|
'trusted-publishing-limits',
|
||||||
|
'remediation',
|
||||||
|
];
|
||||||
|
const missingSignals = requiredSignals.filter(signal => !signals.has(signal));
|
||||||
|
checks.push(createCheck(
|
||||||
|
'advisory-signal-coverage',
|
||||||
|
missingSignals.length === 0 ? 'pass' : 'fail',
|
||||||
|
missingSignals.length === 0
|
||||||
|
? 'sources cover package versions, persistence hooks, provenance limits, and remediation'
|
||||||
|
: `missing signal coverage: ${missingSignals.join(', ')}`,
|
||||||
|
'Update the source registry before adding or removing scanner indicators.',
|
||||||
|
));
|
||||||
|
|
||||||
|
return checks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshStatusFromResult(result) {
|
||||||
|
if (result && result.ok) {
|
||||||
|
return {
|
||||||
|
status: 'ok',
|
||||||
|
statusCode: result.statusCode || null,
|
||||||
|
finalUrl: result.finalUrl || null,
|
||||||
|
checkedAt: result.checkedAt || null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'warning',
|
||||||
|
statusCode: result && result.statusCode ? result.statusCode : null,
|
||||||
|
finalUrl: result && result.finalUrl ? result.finalUrl : null,
|
||||||
|
checkedAt: result && result.checkedAt ? result.checkedAt : null,
|
||||||
|
error: result && result.error ? String(result.error) : 'source refresh failed',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function defaultFetchSource(source, options = {}) {
|
||||||
|
const checkedAt = options.checkedAt || DEFAULT_GENERATED_AT();
|
||||||
|
try {
|
||||||
|
const result = await requestUrl(source.url, {
|
||||||
|
timeoutMs: options.timeoutMs || DEFAULT_TIMEOUT_MS,
|
||||||
|
redirectsRemaining: MAX_REDIRECTS,
|
||||||
|
method: 'HEAD',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.statusCode === 405 || result.statusCode === 403) {
|
||||||
|
return requestUrl(source.url, {
|
||||||
|
timeoutMs: options.timeoutMs || DEFAULT_TIMEOUT_MS,
|
||||||
|
redirectsRemaining: MAX_REDIRECTS,
|
||||||
|
method: 'GET',
|
||||||
|
checkedAt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...result, checkedAt };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
statusCode: null,
|
||||||
|
finalUrl: source.url,
|
||||||
|
checkedAt,
|
||||||
|
error: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestUrl(url, options) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
const client = parsed.protocol === 'http:' ? http : https;
|
||||||
|
const request = client.request(parsed, {
|
||||||
|
method: options.method || 'HEAD',
|
||||||
|
timeout: options.timeoutMs || DEFAULT_TIMEOUT_MS,
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'ecc-supply-chain-watch/2.0',
|
||||||
|
Accept: 'text/html,application/json;q=0.9,*/*;q=0.8',
|
||||||
|
},
|
||||||
|
}, response => {
|
||||||
|
const statusCode = response.statusCode || 0;
|
||||||
|
const location = response.headers.location;
|
||||||
|
if (
|
||||||
|
statusCode >= 300
|
||||||
|
&& statusCode < 400
|
||||||
|
&& location
|
||||||
|
&& options.redirectsRemaining > 0
|
||||||
|
) {
|
||||||
|
response.resume();
|
||||||
|
const nextUrl = new URL(location, parsed).toString();
|
||||||
|
resolve(requestUrl(nextUrl, {
|
||||||
|
...options,
|
||||||
|
redirectsRemaining: options.redirectsRemaining - 1,
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.resume();
|
||||||
|
response.on('end', () => {
|
||||||
|
resolve({
|
||||||
|
ok: statusCode >= 200 && statusCode < 400,
|
||||||
|
statusCode,
|
||||||
|
finalUrl: url,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
request.on('timeout', () => {
|
||||||
|
request.destroy(new Error(`timed out after ${options.timeoutMs || DEFAULT_TIMEOUT_MS}ms`));
|
||||||
|
});
|
||||||
|
|
||||||
|
request.on('error', error => {
|
||||||
|
resolve({
|
||||||
|
ok: false,
|
||||||
|
statusCode: null,
|
||||||
|
finalUrl: url,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
request.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLinearStatus(report, sources) {
|
||||||
|
const primaryEvidence = sources
|
||||||
|
.filter(source => [
|
||||||
|
'primary-incident-postmortem',
|
||||||
|
'security-advisory',
|
||||||
|
'vendor-response',
|
||||||
|
'incident-analysis',
|
||||||
|
].includes(source.sourceType))
|
||||||
|
.slice(0, 5)
|
||||||
|
.map(source => `${source.publisher}: ${source.title}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
issueId: 'ITO-57',
|
||||||
|
status: 'in_progress',
|
||||||
|
summary: report.ready
|
||||||
|
? 'Advisory sources current; scheduled supply-chain watch now emits source refresh evidence.'
|
||||||
|
: 'Advisory source coverage needs repair before release readiness.',
|
||||||
|
evidence: primaryEvidence,
|
||||||
|
remaining: 'Linear status synchronization still needs a live connector/status-update pass after each significant merge batch.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildAdvisorySourceReport(options = {}) {
|
||||||
|
const generatedAt = options.generatedAt || DEFAULT_GENERATED_AT();
|
||||||
|
const sources = (options.sources || DEFAULT_ADVISORY_SOURCES).map(source => ({
|
||||||
|
...source,
|
||||||
|
ecosystems: normalizeArray(source.ecosystems),
|
||||||
|
signals: normalizeArray(source.signals),
|
||||||
|
}));
|
||||||
|
const checks = validateSources(sources);
|
||||||
|
const refreshEnabled = Boolean(options.refresh);
|
||||||
|
const fetchSource = options.fetchSource || defaultFetchSource;
|
||||||
|
let refreshWarnings = 0;
|
||||||
|
|
||||||
|
const reportSources = [];
|
||||||
|
for (const source of sources) {
|
||||||
|
let refreshStatus = { status: 'not_requested' };
|
||||||
|
if (refreshEnabled && source.refresh !== false) {
|
||||||
|
const result = await fetchSource(source, {
|
||||||
|
timeoutMs: options.timeoutMs || DEFAULT_TIMEOUT_MS,
|
||||||
|
checkedAt: generatedAt,
|
||||||
|
});
|
||||||
|
refreshStatus = refreshStatusFromResult(result);
|
||||||
|
if (refreshStatus.status !== 'ok') refreshWarnings += 1;
|
||||||
|
}
|
||||||
|
reportSources.push({ ...source, refreshStatus });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshEnabled) {
|
||||||
|
checks.push(createCheck(
|
||||||
|
'advisory-refresh',
|
||||||
|
refreshWarnings === 0 ? 'pass' : 'warn',
|
||||||
|
refreshWarnings === 0
|
||||||
|
? 'all advisory source URLs responded during refresh'
|
||||||
|
: `${refreshWarnings} advisory source URL(s) returned warnings during refresh`,
|
||||||
|
'Review warning sources manually before changing IOC coverage or release evidence.',
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
checks.push(createCheck(
|
||||||
|
'advisory-refresh',
|
||||||
|
'pass',
|
||||||
|
'live advisory refresh not requested for this offline source contract report',
|
||||||
|
'Run with --refresh in the scheduled watch to capture live URL status evidence.',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ready = checks.every(check => check.status !== 'fail');
|
||||||
|
const report = {
|
||||||
|
schema_version: 'ecc.supply-chain-advisory-sources.v1',
|
||||||
|
generatedAt,
|
||||||
|
ready,
|
||||||
|
refresh: {
|
||||||
|
enabled: refreshEnabled,
|
||||||
|
ok: refreshEnabled ? refreshWarnings === 0 : null,
|
||||||
|
warningCount: refreshWarnings,
|
||||||
|
},
|
||||||
|
sources: reportSources,
|
||||||
|
checks,
|
||||||
|
};
|
||||||
|
|
||||||
|
report.linear = {
|
||||||
|
status: buildLinearStatus(report, reportSources),
|
||||||
|
};
|
||||||
|
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const options = {};
|
||||||
|
for (let i = 0; i < argv.length; i += 1) {
|
||||||
|
const arg = argv[i];
|
||||||
|
if (arg === '--help' || arg === '-h') {
|
||||||
|
options.help = true;
|
||||||
|
} else if (arg === '--json') {
|
||||||
|
options.json = true;
|
||||||
|
} else if (arg === '--refresh') {
|
||||||
|
options.refresh = true;
|
||||||
|
} else if (arg === '--strict-refresh') {
|
||||||
|
options.strictRefresh = true;
|
||||||
|
options.refresh = true;
|
||||||
|
} else if (arg === '--generated-at') {
|
||||||
|
options.generatedAt = argv[++i];
|
||||||
|
} else if (arg === '--timeout-ms') {
|
||||||
|
options.timeoutMs = Number(argv[++i]);
|
||||||
|
if (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0) {
|
||||||
|
throw new Error('--timeout-ms must be a positive number');
|
||||||
|
}
|
||||||
|
} else if (arg === '--write') {
|
||||||
|
options.writePath = argv[++i];
|
||||||
|
if (!options.writePath) throw new Error('--write requires a path');
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unknown argument: ${arg}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printHelp() {
|
||||||
|
console.log(`Usage: node scripts/ci/supply-chain-advisory-sources.js [options]
|
||||||
|
|
||||||
|
Build the active supply-chain advisory source report used by the scheduled
|
||||||
|
watch workflow and Linear ITO-57 status updates.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--json Emit JSON instead of text
|
||||||
|
--refresh Check source URLs and record warning status
|
||||||
|
--strict-refresh Fail when a refreshed source URL returns a warning
|
||||||
|
--generated-at <ts> Override the report timestamp
|
||||||
|
--timeout-ms <n> Per-source refresh timeout (default: ${DEFAULT_TIMEOUT_MS})
|
||||||
|
--write <path> Write the report to a file
|
||||||
|
--help, -h Show this help
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderText(report) {
|
||||||
|
const lines = [
|
||||||
|
`Supply-chain advisory sources: ${report.ready ? 'ready' : 'blocked'}`,
|
||||||
|
`Sources: ${report.sources.length}`,
|
||||||
|
`Refresh: ${report.refresh.enabled ? (report.refresh.ok ? 'ok' : `warnings=${report.refresh.warningCount}`) : 'not requested'}`,
|
||||||
|
`Linear ${report.linear.status.issueId}: ${report.linear.status.summary}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const check of report.checks) {
|
||||||
|
lines.push(`- ${check.status.toUpperCase()} ${check.id}: ${check.summary}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${lines.join('\n')}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeReport(report, writePath) {
|
||||||
|
const absolutePath = path.resolve(writePath);
|
||||||
|
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
|
||||||
|
fs.writeFileSync(absolutePath, `${JSON.stringify(report, null, 2)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const options = parseArgs(process.argv.slice(2));
|
||||||
|
if (options.help) {
|
||||||
|
printHelp();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const report = await buildAdvisorySourceReport(options);
|
||||||
|
if (options.writePath) writeReport(report, options.writePath);
|
||||||
|
|
||||||
|
if (options.json) {
|
||||||
|
console.log(JSON.stringify(report, null, 2));
|
||||||
|
} else {
|
||||||
|
process.stdout.write(renderText(report));
|
||||||
|
}
|
||||||
|
|
||||||
|
const failed = !report.ready || (options.strictRefresh && report.refresh.enabled && !report.refresh.ok);
|
||||||
|
process.exit(failed ? 1 : 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
DEFAULT_ADVISORY_SOURCES,
|
||||||
|
buildAdvisorySourceReport,
|
||||||
|
parseArgs,
|
||||||
|
renderText,
|
||||||
|
};
|
||||||
@@ -52,7 +52,7 @@ function buildContextBar(remaining) {
|
|||||||
if (used < 50) return ` \x1b[32m${bar} ${used}%\x1b[0m`;
|
if (used < 50) return ` \x1b[32m${bar} ${used}%\x1b[0m`;
|
||||||
if (used < 65) return ` \x1b[33m${bar} ${used}%\x1b[0m`;
|
if (used < 65) return ` \x1b[33m${bar} ${used}%\x1b[0m`;
|
||||||
if (used < 80) return ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
|
if (used < 80) return ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
|
||||||
return ` \x1b[5;31m${bar} ${used}%\x1b[0m`;
|
return ` \x1b[1;31m${bar} ${used}%\x1b[0m`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,7 +137,7 @@ function runStatusline() {
|
|||||||
parts.push(dur);
|
parts.push(dur);
|
||||||
}
|
}
|
||||||
if (parts.length > 0) {
|
if (parts.length > 0) {
|
||||||
metricsStr = `\x1b[36m${parts.join(' ')}\x1b[0m`;
|
metricsStr = `\x1b[38;5;117m${parts.join(' ')}\x1b[0m`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ function runStatusline() {
|
|||||||
const segments = [`\x1b[2m${model}\x1b[0m`];
|
const segments = [`\x1b[2m${model}\x1b[0m`];
|
||||||
|
|
||||||
if (task) {
|
if (task) {
|
||||||
segments.push(`\x1b[1m${task}\x1b[0m`);
|
segments.push(`\x1b[1;97m${task}\x1b[0m`);
|
||||||
}
|
}
|
||||||
if (metricsStr) {
|
if (metricsStr) {
|
||||||
segments.push(metricsStr);
|
segments.push(metricsStr);
|
||||||
|
|||||||
660
scripts/operator-readiness-dashboard.js
Normal file
660
scripts/operator-readiness-dashboard.js
Normal file
@@ -0,0 +1,660 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { spawnSync } = require('child_process');
|
||||||
|
const { buildReport: buildPlatformReport } = require('./platform-audit');
|
||||||
|
|
||||||
|
const SCHEMA_VERSION = 'ecc.operator-readiness-dashboard.v1';
|
||||||
|
const DEFAULT_THRESHOLDS = Object.freeze({
|
||||||
|
maxOpenPrs: 20,
|
||||||
|
maxOpenIssues: 20,
|
||||||
|
maxDirtyFiles: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
console.log([
|
||||||
|
'Usage: node scripts/operator-readiness-dashboard.js [options]',
|
||||||
|
'',
|
||||||
|
'Generate the ECC operator readiness dashboard and prompt-to-artifact audit.',
|
||||||
|
'',
|
||||||
|
'Options:',
|
||||||
|
' --format <text|json|markdown>',
|
||||||
|
' Output format (default: markdown)',
|
||||||
|
' --json Alias for --format json',
|
||||||
|
' --markdown Alias for --format markdown',
|
||||||
|
' --write <path> Write json or markdown output to a file',
|
||||||
|
' --root <dir> Repository root to inspect (default: cwd)',
|
||||||
|
' --repo <owner/repo> GitHub repo to inspect; repeatable',
|
||||||
|
' --skip-github Skip live GitHub queue/discussion checks',
|
||||||
|
' --max-open-prs <n> PR budget passed through to platform:audit',
|
||||||
|
' --max-open-issues <n> Issue budget passed through to platform:audit',
|
||||||
|
' --max-dirty-files <n> Dirty-file budget passed through to platform:audit',
|
||||||
|
' --allow-untracked <path> Ignore untracked files under path; repeatable',
|
||||||
|
' --use-env-github-token Keep GITHUB_TOKEN when invoking gh',
|
||||||
|
' --generated-at <iso> Override generatedAt for deterministic tests',
|
||||||
|
' --exit-code Return 2 when the objective is not ready',
|
||||||
|
' --help, -h Show this help',
|
||||||
|
].join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function readValue(args, index, flagName) {
|
||||||
|
const value = args[index + 1];
|
||||||
|
if (!value || value.startsWith('--')) {
|
||||||
|
throw new Error(`${flagName} requires a value`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseIntegerFlag(value, flagName) {
|
||||||
|
const parsed = Number.parseInt(value, 10);
|
||||||
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
||||||
|
throw new Error(`Invalid ${flagName}: ${value}`);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeRelativePrefix(value) {
|
||||||
|
const normalized = String(value || '')
|
||||||
|
.replace(/\\/g, '/')
|
||||||
|
.replace(/^\.\/+/, '')
|
||||||
|
.replace(/\/+$/, '');
|
||||||
|
return normalized ? `${normalized}/` : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const args = argv.slice(2);
|
||||||
|
const parsed = {
|
||||||
|
allowUntracked: [],
|
||||||
|
exitCode: false,
|
||||||
|
format: 'markdown',
|
||||||
|
generatedAt: null,
|
||||||
|
help: false,
|
||||||
|
repos: [],
|
||||||
|
root: path.resolve(process.cwd()),
|
||||||
|
skipGithub: false,
|
||||||
|
thresholds: { ...DEFAULT_THRESHOLDS },
|
||||||
|
useEnvGithubToken: false,
|
||||||
|
writePath: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let index = 0; index < args.length; index += 1) {
|
||||||
|
const arg = args[index];
|
||||||
|
|
||||||
|
if (arg === '--help' || arg === '-h') {
|
||||||
|
parsed.help = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--format') {
|
||||||
|
parsed.format = readValue(args, index, arg).toLowerCase();
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith('--format=')) {
|
||||||
|
parsed.format = arg.slice('--format='.length).toLowerCase();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--json') {
|
||||||
|
parsed.format = 'json';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--markdown') {
|
||||||
|
parsed.format = 'markdown';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--write') {
|
||||||
|
parsed.writePath = path.resolve(readValue(args, index, arg));
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith('--write=')) {
|
||||||
|
parsed.writePath = path.resolve(arg.slice('--write='.length));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--root') {
|
||||||
|
parsed.root = path.resolve(readValue(args, index, arg));
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith('--root=')) {
|
||||||
|
parsed.root = path.resolve(arg.slice('--root='.length));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--repo') {
|
||||||
|
parsed.repos.push(readValue(args, index, arg));
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith('--repo=')) {
|
||||||
|
parsed.repos.push(arg.slice('--repo='.length));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--skip-github') {
|
||||||
|
parsed.skipGithub = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--allow-untracked') {
|
||||||
|
parsed.allowUntracked.push(readValue(args, index, arg));
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith('--allow-untracked=')) {
|
||||||
|
parsed.allowUntracked.push(arg.slice('--allow-untracked='.length));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--max-open-prs') {
|
||||||
|
parsed.thresholds.maxOpenPrs = parseIntegerFlag(readValue(args, index, arg), arg);
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith('--max-open-prs=')) {
|
||||||
|
parsed.thresholds.maxOpenPrs = parseIntegerFlag(arg.slice('--max-open-prs='.length), '--max-open-prs');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--max-open-issues') {
|
||||||
|
parsed.thresholds.maxOpenIssues = parseIntegerFlag(readValue(args, index, arg), arg);
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith('--max-open-issues=')) {
|
||||||
|
parsed.thresholds.maxOpenIssues = parseIntegerFlag(arg.slice('--max-open-issues='.length), '--max-open-issues');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--max-dirty-files') {
|
||||||
|
parsed.thresholds.maxDirtyFiles = parseIntegerFlag(readValue(args, index, arg), arg);
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith('--max-dirty-files=')) {
|
||||||
|
parsed.thresholds.maxDirtyFiles = parseIntegerFlag(arg.slice('--max-dirty-files='.length), '--max-dirty-files');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--use-env-github-token') {
|
||||||
|
parsed.useEnvGithubToken = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--generated-at') {
|
||||||
|
parsed.generatedAt = readValue(args, index, arg);
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith('--generated-at=')) {
|
||||||
|
parsed.generatedAt = arg.slice('--generated-at='.length);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--exit-code') {
|
||||||
|
parsed.exitCode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unknown argument: ${arg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!['text', 'json', 'markdown'].includes(parsed.format)) {
|
||||||
|
throw new Error(`Invalid format: ${parsed.format}. Use text, json, or markdown.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.writePath && parsed.format === 'text') {
|
||||||
|
throw new Error('--write requires --json, --markdown, or --format json|markdown');
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed.allowUntracked = parsed.allowUntracked.map(normalizeRelativePrefix).filter(Boolean);
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readText(rootDir, relativePath) {
|
||||||
|
try {
|
||||||
|
return fs.readFileSync(path.join(rootDir, relativePath), 'utf8');
|
||||||
|
} catch (_error) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileExists(rootDir, relativePath) {
|
||||||
|
return fs.existsSync(path.join(rootDir, relativePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
function includesAll(text, needles) {
|
||||||
|
return needles.every(needle => text.includes(needle));
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCommand(command, args, options = {}) {
|
||||||
|
const result = spawnSync(command, args, {
|
||||||
|
cwd: options.cwd,
|
||||||
|
encoding: 'utf8',
|
||||||
|
maxBuffer: 10 * 1024 * 1024,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error || result.status !== 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result.stdout || '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPackage(rootDir) {
|
||||||
|
const text = readText(rootDir, 'package.json');
|
||||||
|
if (!text.trim()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(text);
|
||||||
|
} catch (_error) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRequirement(id, requirement, artifact, status, evidence, gap) {
|
||||||
|
return { id, requirement, artifact, status, evidence, gap };
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCurrentOrComplete(status) {
|
||||||
|
return status === 'current' || status === 'complete';
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRequirements(rootDir, platformReport) {
|
||||||
|
const roadmap = readText(rootDir, 'docs/ECC-2.0-GA-ROADMAP.md');
|
||||||
|
const publicationReadiness = readText(rootDir, 'docs/releases/2.0.0-rc.1/publication-readiness.md');
|
||||||
|
const namingMatrix = readText(rootDir, 'docs/releases/2.0.0-rc.1/naming-and-publication-matrix.md');
|
||||||
|
const previewManifest = readText(rootDir, 'docs/releases/2.0.0-rc.1/preview-pack-manifest.md');
|
||||||
|
const progressSync = readText(rootDir, 'docs/architecture/progress-sync-contract.md');
|
||||||
|
const observabilityReadiness = readText(rootDir, 'docs/architecture/observability-readiness.md');
|
||||||
|
const stalePrSalvage = readText(rootDir, 'docs/stale-pr-salvage-ledger.md');
|
||||||
|
const supplyChainRunbook = readText(rootDir, 'docs/security/supply-chain-incident-response.md');
|
||||||
|
const supplyChainWorkflow = readText(rootDir, '.github/workflows/supply-chain-watch.yml');
|
||||||
|
const packageJson = readPackage(rootDir);
|
||||||
|
const scripts = packageJson.scripts || {};
|
||||||
|
|
||||||
|
const githubLive = !platformReport.github.skipped && platformReport.github.totals.errors === 0;
|
||||||
|
const queuesCurrent = githubLive
|
||||||
|
&& platformReport.github.totals.openPrs <= platformReport.thresholds.maxOpenPrs
|
||||||
|
&& platformReport.github.totals.openIssues <= platformReport.thresholds.maxOpenIssues;
|
||||||
|
const discussionsCurrent = githubLive
|
||||||
|
&& platformReport.github.totals.discussionsNeedingMaintainerTouch === 0
|
||||||
|
&& platformReport.github.totals.discussionsMissingAcceptedAnswer === 0;
|
||||||
|
|
||||||
|
return [
|
||||||
|
buildRequirement(
|
||||||
|
'public-pr-budget',
|
||||||
|
'Keep public PRs below 20',
|
||||||
|
'scripts/platform-audit.js live GitHub sweep',
|
||||||
|
queuesCurrent ? 'current' : 'in_progress',
|
||||||
|
githubLive
|
||||||
|
? `${platformReport.github.totals.openPrs} open PRs across ${platformReport.github.repos.length} tracked repos`
|
||||||
|
: 'live GitHub queue readback was skipped or failed',
|
||||||
|
queuesCurrent ? 'repeat before release' : 'run live platform:audit and drain PR queue'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'public-issue-budget',
|
||||||
|
'Keep public issues below 20',
|
||||||
|
'scripts/platform-audit.js live GitHub sweep',
|
||||||
|
queuesCurrent ? 'current' : 'in_progress',
|
||||||
|
githubLive
|
||||||
|
? `${platformReport.github.totals.openIssues} open issues across ${platformReport.github.repos.length} tracked repos`
|
||||||
|
: 'live GitHub queue readback was skipped or failed',
|
||||||
|
queuesCurrent ? 'repeat before release' : 'run live platform:audit and drain issue queue'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'repository-discussions',
|
||||||
|
'Respond and manage repository discussions',
|
||||||
|
'scripts/platform-audit.js discussion summary',
|
||||||
|
discussionsCurrent ? 'current' : 'in_progress',
|
||||||
|
githubLive
|
||||||
|
? `${platformReport.github.totals.discussionsNeedingMaintainerTouch} need maintainer touch; ${platformReport.github.totals.discussionsMissingAcceptedAnswer} answerable discussions missing accepted answer`
|
||||||
|
: 'live discussion readback was skipped or failed',
|
||||||
|
discussionsCurrent ? 'repeat before release' : 'respond, answer, or route remaining discussions'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'completion-dashboard',
|
||||||
|
'Build ITO-44 completion dashboard into a repeatable command',
|
||||||
|
'npm run operator:dashboard',
|
||||||
|
scripts['operator:dashboard'] === 'node scripts/operator-readiness-dashboard.js'
|
||||||
|
&& fileExists(rootDir, 'scripts/operator-readiness-dashboard.js')
|
||||||
|
? 'complete'
|
||||||
|
: 'in_progress',
|
||||||
|
scripts['operator:dashboard'] === 'node scripts/operator-readiness-dashboard.js'
|
||||||
|
? 'operator:dashboard package script exists'
|
||||||
|
: 'operator:dashboard package script missing',
|
||||||
|
'keep generated dashboard attached to publication evidence'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'ecc-preview-pack',
|
||||||
|
'ECC 2.0 preview pack ready',
|
||||||
|
'docs/releases/2.0.0-rc.1/preview-pack-manifest.md',
|
||||||
|
includesAll(previewManifest, ['publication-readiness.md', 'release-notes.md', 'quickstart.md']) ? 'in_progress' : 'not_complete',
|
||||||
|
includesAll(previewManifest, ['publication-readiness.md', 'release-notes.md', 'quickstart.md'])
|
||||||
|
? 'preview pack manifest is in-tree'
|
||||||
|
: 'preview pack manifest is incomplete',
|
||||||
|
'final clean-checkout release approval and publish evidence still pending'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'hermes-specialized-skills',
|
||||||
|
'Include Hermes specialized skills safely',
|
||||||
|
'docs/HERMES-SETUP.md and skills/hermes-imports/SKILL.md',
|
||||||
|
fileExists(rootDir, 'docs/HERMES-SETUP.md') && fileExists(rootDir, 'skills/hermes-imports/SKILL.md')
|
||||||
|
? 'in_progress'
|
||||||
|
: 'not_complete',
|
||||||
|
fileExists(rootDir, 'docs/HERMES-SETUP.md') && fileExists(rootDir, 'skills/hermes-imports/SKILL.md')
|
||||||
|
? 'Hermes setup and import skill are present'
|
||||||
|
: 'Hermes setup/import artifacts missing',
|
||||||
|
'final preview-pack smoke and release review pending'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'naming-and-plugin-publication',
|
||||||
|
'Prepare name-change, Claude plugin, and Codex plugin paths',
|
||||||
|
'naming-and-publication-matrix plus publication-readiness',
|
||||||
|
includesAll(namingMatrix, ['Claude plugin', 'Codex plugin', 'npm package', 'Publication Paths'])
|
||||||
|
&& includesAll(publicationReadiness, ['Claude plugin', 'Codex plugin'])
|
||||||
|
? 'in_progress'
|
||||||
|
: 'not_complete',
|
||||||
|
'naming matrix and plugin readiness gates exist',
|
||||||
|
'real tag/push, marketplace submission, and final channel choice remain approval-gated'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'release-notes-and-notifications',
|
||||||
|
'Prepare release notes, articles, tweets, and push notifications',
|
||||||
|
'docs/releases/2.0.0-rc.1 social and release-copy files',
|
||||||
|
fileExists(rootDir, 'docs/releases/2.0.0-rc.1/release-notes.md')
|
||||||
|
&& fileExists(rootDir, 'docs/releases/2.0.0-rc.1/x-thread.md')
|
||||||
|
&& fileExists(rootDir, 'docs/releases/2.0.0-rc.1/linkedin-post.md')
|
||||||
|
? 'in_progress'
|
||||||
|
: 'not_complete',
|
||||||
|
'release notes, X thread, and LinkedIn draft are present',
|
||||||
|
'URL-backed refresh and publish approval still pending'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'agentshield-enterprise-iteration',
|
||||||
|
'Advance AgentShield enterprise iteration',
|
||||||
|
'AgentShield PR evidence plus enterprise roadmap',
|
||||||
|
roadmap.includes('AgentShield Enterprise Iteration') && roadmap.includes('#78-#89')
|
||||||
|
? 'in_progress'
|
||||||
|
: 'not_complete',
|
||||||
|
'AgentShield enterprise PR evidence is mirrored in the GA roadmap',
|
||||||
|
'cross-harness policy integration and richer fleet-review workflow remain pending after fleet-summary consumption and evidence-path surfacing shipped'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'ecc-tools-next-level',
|
||||||
|
'Advance ECC Tools native payments and AI-native harness-agnostic app',
|
||||||
|
'ECC Tools PR evidence, billing gate, hosted analysis lanes',
|
||||||
|
includesAll(roadmap, ['ECC-Tools PR #77', 'hosted promotion', 'announcementGate'])
|
||||||
|
? 'in_progress'
|
||||||
|
: 'not_complete',
|
||||||
|
'billing announcement gate, hosted analysis lanes, AgentShield fleet-summary consumption, and hosted finding evidence paths are mirrored in the GA roadmap',
|
||||||
|
'live Marketplace test-account readback, hosted promotion telemetry, and richer operator review UX pending'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'legacy-salvage',
|
||||||
|
'Audit, prune, or attach legacy work',
|
||||||
|
'docs/stale-pr-salvage-ledger.md and legacy inventory',
|
||||||
|
stalePrSalvage.includes('Manual review tail') || roadmap.includes('ITO-55')
|
||||||
|
? 'in_progress'
|
||||||
|
: 'not_complete',
|
||||||
|
'legacy salvage ledger and ITO-55 tracking are present',
|
||||||
|
'final translation/manual-review tail remains'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'linear-roadmap-and-progress',
|
||||||
|
'Keep Linear roadmap detailed and progress tracking synchronized',
|
||||||
|
'Linear project mirror plus progress-sync contract',
|
||||||
|
includesAll(roadmap, ['ITO-44', 'ITO-59', 'Linear']) && includesAll(progressSync, ['GitHub', 'Linear', 'handoff', 'repo roadmap'])
|
||||||
|
? 'in_progress'
|
||||||
|
: 'not_complete',
|
||||||
|
'repo mirror and progress-sync contract are present',
|
||||||
|
'recurring Linear status sync and productized realtime sync remain pending'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'observability-for-self-use',
|
||||||
|
'Provide ECC 2.0 observability for self-use',
|
||||||
|
'observability readiness gate',
|
||||||
|
scripts['observability:ready'] === 'node scripts/observability-readiness.js'
|
||||||
|
&& includesAll(observabilityReadiness, ['observability-readiness.js'])
|
||||||
|
? 'complete'
|
||||||
|
: 'in_progress',
|
||||||
|
scripts['observability:ready'] === 'node scripts/observability-readiness.js'
|
||||||
|
? 'observability:ready command and readiness doc exist'
|
||||||
|
: 'observability readiness command missing',
|
||||||
|
'runtime/dashboard implementation can continue after release gates'
|
||||||
|
),
|
||||||
|
buildRequirement(
|
||||||
|
'supply-chain-local-protection',
|
||||||
|
'Keep Mini Shai-Hulud/TanStack protection loop current',
|
||||||
|
'supply-chain watch plus runbook',
|
||||||
|
includesAll(supplyChainRunbook, ['TanStack', 'Mini Shai-Hulud', 'scan-supply-chain-iocs.js', 'supply-chain-advisory-sources.js'])
|
||||||
|
&& includesAll(supplyChainWorkflow, ['supply-chain-advisory-sources.js', 'supply-chain-advisory-sources.json'])
|
||||||
|
&& scripts['security:advisory-sources'] === 'node scripts/ci/supply-chain-advisory-sources.js'
|
||||||
|
&& fileExists(rootDir, '.github/workflows/supply-chain-watch.yml')
|
||||||
|
? 'current'
|
||||||
|
: 'in_progress',
|
||||||
|
scripts['security:advisory-sources'] === 'node scripts/ci/supply-chain-advisory-sources.js'
|
||||||
|
? 'scheduled supply-chain watch now emits IOC and advisory-source refresh artifacts'
|
||||||
|
: 'scheduled supply-chain watch or advisory-source command is missing',
|
||||||
|
'Linear status synchronization remains ITO-57 follow-up after each significant merge batch'
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildReport(options) {
|
||||||
|
const rootDir = path.resolve(options.root);
|
||||||
|
const generatedAt = options.generatedAt || new Date().toISOString();
|
||||||
|
const platformReport = buildPlatformReport({
|
||||||
|
allowUntracked: options.allowUntracked,
|
||||||
|
exitCode: false,
|
||||||
|
format: 'json',
|
||||||
|
help: false,
|
||||||
|
repos: options.repos,
|
||||||
|
root: rootDir,
|
||||||
|
skipGithub: options.skipGithub,
|
||||||
|
thresholds: options.thresholds,
|
||||||
|
useEnvGithubToken: options.useEnvGithubToken,
|
||||||
|
writePath: null,
|
||||||
|
});
|
||||||
|
const requirements = buildRequirements(rootDir, platformReport);
|
||||||
|
const incompleteRequirements = requirements.filter(item => !isCurrentOrComplete(item.status));
|
||||||
|
const topActions = incompleteRequirements.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
summary: item.requirement,
|
||||||
|
fix: item.gap,
|
||||||
|
}));
|
||||||
|
const head = runCommand('git', ['rev-parse', 'HEAD'], { cwd: rootDir });
|
||||||
|
|
||||||
|
return {
|
||||||
|
schema_version: SCHEMA_VERSION,
|
||||||
|
generatedAt,
|
||||||
|
root: rootDir,
|
||||||
|
head,
|
||||||
|
ready: incompleteRequirements.length === 0,
|
||||||
|
dashboardReady: platformReport.ready,
|
||||||
|
publicationReady: false,
|
||||||
|
platform: {
|
||||||
|
ready: platformReport.ready,
|
||||||
|
branch: platformReport.git.branch,
|
||||||
|
blockingDirtyCount: platformReport.git.blockingDirtyCount,
|
||||||
|
ignoredDirtyCount: platformReport.git.ignoredDirty.length,
|
||||||
|
openPrs: platformReport.github.totals.openPrs,
|
||||||
|
openIssues: platformReport.github.totals.openIssues,
|
||||||
|
discussionsNeedingMaintainerTouch: platformReport.github.totals.discussionsNeedingMaintainerTouch,
|
||||||
|
discussionsMissingAcceptedAnswer: platformReport.github.totals.discussionsMissingAcceptedAnswer,
|
||||||
|
githubErrors: platformReport.github.totals.errors,
|
||||||
|
githubSkipped: platformReport.github.skipped,
|
||||||
|
},
|
||||||
|
requirements,
|
||||||
|
top_actions: topActions,
|
||||||
|
next_work_order: [
|
||||||
|
'Regenerate this dashboard from the final release commit before publication evidence is recorded.',
|
||||||
|
'Continue ITO-57 with Linear status synchronization for the scheduled supply-chain watch advisory-source report.',
|
||||||
|
'Advance ECC Tools live Marketplace test-account readback before publishing native-payments announcement copy.',
|
||||||
|
'Resume ITO-45, ITO-46, and ITO-56 only after the generated dashboard and final release gates are refreshed.',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function markdownEscape(value) {
|
||||||
|
return String(value === undefined || value === null ? '' : value)
|
||||||
|
.replace(/\|/g, '\\|')
|
||||||
|
.replace(/\r?\n/g, '<br>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderText(report) {
|
||||||
|
const lines = [
|
||||||
|
`ECC Operator Readiness Dashboard: ${report.ready ? 'objective ready' : 'work remaining'}`,
|
||||||
|
`Generated: ${report.generatedAt}`,
|
||||||
|
`Commit: ${report.head || 'unknown'}`,
|
||||||
|
`Dashboard ready: ${report.dashboardReady}`,
|
||||||
|
`Publication ready: ${report.publicationReady}`,
|
||||||
|
'',
|
||||||
|
'Platform:',
|
||||||
|
` PRs: ${report.platform.openPrs}`,
|
||||||
|
` Issues: ${report.platform.openIssues}`,
|
||||||
|
` Discussions needing touch: ${report.platform.discussionsNeedingMaintainerTouch}`,
|
||||||
|
` Missing accepted answers: ${report.platform.discussionsMissingAcceptedAnswer}`,
|
||||||
|
` Blocking dirty files: ${report.platform.blockingDirtyCount}`,
|
||||||
|
'',
|
||||||
|
'Requirements:',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const item of report.requirements) {
|
||||||
|
lines.push(` ${item.status.toUpperCase()} ${item.id}: ${item.requirement}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push('', 'Top actions:');
|
||||||
|
if (report.top_actions.length === 0) {
|
||||||
|
lines.push(' none');
|
||||||
|
} else {
|
||||||
|
for (const action of report.top_actions) {
|
||||||
|
lines.push(` - ${action.id}: ${action.fix}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${lines.join('\n')}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMarkdown(report) {
|
||||||
|
const lines = [
|
||||||
|
'# ECC Operator Readiness Dashboard',
|
||||||
|
'',
|
||||||
|
'This dashboard is generated by `npm run operator:dashboard`. It is an operator snapshot, not release approval.',
|
||||||
|
'',
|
||||||
|
`Generated: ${report.generatedAt}`,
|
||||||
|
`Commit: ${report.head || 'unknown'}`,
|
||||||
|
`Status: ${report.ready ? 'objective ready' : 'work remaining'}`,
|
||||||
|
'',
|
||||||
|
'## Current Status',
|
||||||
|
'',
|
||||||
|
'| Area | Status | Evidence |',
|
||||||
|
'| --- | --- | --- |',
|
||||||
|
`| PR queue | ${report.platform.openPrs < 20 && !report.platform.githubSkipped ? 'Current' : 'Needs work'} | ${report.platform.openPrs} open PRs across tracked repos |`,
|
||||||
|
`| Issue queue | ${report.platform.openIssues < 20 && !report.platform.githubSkipped ? 'Current' : 'Needs work'} | ${report.platform.openIssues} open issues across tracked repos |`,
|
||||||
|
`| Discussions | ${report.platform.discussionsNeedingMaintainerTouch === 0 && report.platform.discussionsMissingAcceptedAnswer === 0 && !report.platform.githubSkipped ? 'Current' : 'Needs work'} | ${report.platform.discussionsNeedingMaintainerTouch} need maintainer touch; ${report.platform.discussionsMissingAcceptedAnswer} missing accepted answer |`,
|
||||||
|
`| Local worktree | ${report.platform.blockingDirtyCount === 0 ? 'Current' : 'Needs work'} | ${report.platform.blockingDirtyCount} blocking dirty files; ${report.platform.ignoredDirtyCount} ignored dirty entries |`,
|
||||||
|
`| Dashboard generation | ${report.dashboardReady ? 'Current' : 'Needs work'} | platform audit ready: ${report.platform.ready}; GitHub skipped: ${report.platform.githubSkipped} |`,
|
||||||
|
`| Publication | ${report.publicationReady ? 'Ready' : 'Not complete'} | release, npm, plugin, billing, and announcement gates are tracked below |`,
|
||||||
|
'',
|
||||||
|
'## Prompt-To-Artifact Checklist',
|
||||||
|
'',
|
||||||
|
'| Objective requirement | Artifact or gate | Status | Evidence | Gap |',
|
||||||
|
'| --- | --- | --- | --- | --- |',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const item of report.requirements) {
|
||||||
|
lines.push(`| ${markdownEscape(item.requirement)} | ${markdownEscape(item.artifact)} | ${markdownEscape(item.status)} | ${markdownEscape(item.evidence)} | ${markdownEscape(item.gap)} |`);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push('', '## Top Actions', '');
|
||||||
|
if (report.top_actions.length === 0) {
|
||||||
|
lines.push('- none');
|
||||||
|
} else {
|
||||||
|
for (const action of report.top_actions) {
|
||||||
|
lines.push(`- \`${markdownEscape(action.id)}\`: ${markdownEscape(action.fix)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push('', '## Next Work Order', '');
|
||||||
|
report.next_work_order.forEach((item, index) => {
|
||||||
|
lines.push(`${index + 1}. ${item}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return `${lines.join('\n')}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderReport(report, format) {
|
||||||
|
if (format === 'json') {
|
||||||
|
return `${JSON.stringify(report, null, 2)}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format === 'text') {
|
||||||
|
return renderText(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderMarkdown(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeOutput(writePath, output) {
|
||||||
|
fs.mkdirSync(path.dirname(writePath), { recursive: true });
|
||||||
|
fs.writeFileSync(writePath, output, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
let options;
|
||||||
|
try {
|
||||||
|
options = parseArgs(process.argv);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error: ${error.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.help) {
|
||||||
|
usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const report = buildReport(options);
|
||||||
|
const output = renderReport(report, options.format);
|
||||||
|
|
||||||
|
if (options.writePath) {
|
||||||
|
writeOutput(options.writePath, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(output);
|
||||||
|
|
||||||
|
if (options.exitCode && !report.ready) {
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
buildReport,
|
||||||
|
parseArgs,
|
||||||
|
renderMarkdown,
|
||||||
|
renderReport,
|
||||||
|
renderText,
|
||||||
|
};
|
||||||
@@ -268,6 +268,10 @@ function readText(rootDir, relativePath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fileExists(rootDir, relativePath) {
|
||||||
|
return fs.existsSync(path.join(rootDir, relativePath));
|
||||||
|
}
|
||||||
|
|
||||||
function safeParseJson(text) {
|
function safeParseJson(text) {
|
||||||
if (!text || !text.trim()) {
|
if (!text || !text.trim()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -431,10 +435,20 @@ function buildLocalEvidenceChecks(rootDir) {
|
|||||||
'platform-audit-cli-surface',
|
'platform-audit-cli-surface',
|
||||||
packageScripts['platform:audit'] === 'node scripts/platform-audit.js'
|
packageScripts['platform:audit'] === 'node scripts/platform-audit.js'
|
||||||
&& packageScripts['discussion:audit'] === 'node scripts/discussion-audit.js'
|
&& packageScripts['discussion:audit'] === 'node scripts/discussion-audit.js'
|
||||||
|
&& packageScripts['operator:dashboard'] === 'node scripts/operator-readiness-dashboard.js'
|
||||||
? 'pass'
|
? 'pass'
|
||||||
: 'fail',
|
: 'fail',
|
||||||
'package.json exposes the platform and discussion audit commands',
|
'package.json exposes platform, discussion, and operator dashboard audit commands',
|
||||||
{ fix: 'Add platform:audit and discussion:audit commands to package.json.' }
|
{ fix: 'Add platform:audit, discussion:audit, and operator:dashboard commands to package.json.' }
|
||||||
|
),
|
||||||
|
buildCheck(
|
||||||
|
'operator-dashboard-command',
|
||||||
|
fileExists(rootDir, 'scripts/operator-readiness-dashboard.js')
|
||||||
|
&& packageScripts['operator:dashboard'] === 'node scripts/operator-readiness-dashboard.js'
|
||||||
|
? 'pass'
|
||||||
|
: 'fail',
|
||||||
|
'operator dashboard is generated by the repeatable ITO-44 command',
|
||||||
|
{ path: 'scripts/operator-readiness-dashboard.js' }
|
||||||
),
|
),
|
||||||
buildCheck(
|
buildCheck(
|
||||||
'roadmap-linear-mirror',
|
'roadmap-linear-mirror',
|
||||||
@@ -450,8 +464,11 @@ function buildLocalEvidenceChecks(rootDir) {
|
|||||||
),
|
),
|
||||||
buildCheck(
|
buildCheck(
|
||||||
'supply-chain-runbook',
|
'supply-chain-runbook',
|
||||||
includesAll(supplyChain, ['TanStack', 'Mini Shai-Hulud', 'node-ipc', 'scan-supply-chain-iocs.js']) ? 'pass' : 'fail',
|
includesAll(supplyChain, ['TanStack', 'Mini Shai-Hulud', 'node-ipc', 'scan-supply-chain-iocs.js', 'supply-chain-advisory-sources.js'])
|
||||||
'supply-chain runbook covers the current TanStack/Mini Shai-Hulud/node-ipc scanner lane',
|
&& packageScripts['security:advisory-sources'] === 'node scripts/ci/supply-chain-advisory-sources.js'
|
||||||
|
? 'pass'
|
||||||
|
: 'fail',
|
||||||
|
'supply-chain runbook covers the current TanStack/Mini Shai-Hulud/node-ipc scanner and advisory-source lanes',
|
||||||
{ path: 'docs/security/supply-chain-incident-response.md' }
|
{ path: 'docs/security/supply-chain-incident-response.md' }
|
||||||
),
|
),
|
||||||
buildCheck(
|
buildCheck(
|
||||||
@@ -462,7 +479,13 @@ function buildLocalEvidenceChecks(rootDir) {
|
|||||||
),
|
),
|
||||||
buildCheck(
|
buildCheck(
|
||||||
'operator-readiness-dashboard',
|
'operator-readiness-dashboard',
|
||||||
includesAll(operatorDashboard, ['Prompt-To-Artifact Checklist', 'ITO-44', 'ITO-59', 'PR queue', 'Not complete']) ? 'pass' : 'fail',
|
includesAll(operatorDashboard, [
|
||||||
|
'This dashboard is generated by `npm run operator:dashboard`',
|
||||||
|
'Prompt-To-Artifact Checklist',
|
||||||
|
'PR queue',
|
||||||
|
'Not complete',
|
||||||
|
'Next Work Order',
|
||||||
|
]) ? 'pass' : 'fail',
|
||||||
'operator dashboard maps macro-goal requirements to current evidence and open gaps',
|
'operator dashboard maps macro-goal requirements to current evidence and open gaps',
|
||||||
{ path: 'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-15.md' }
|
{ path: 'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-15.md' }
|
||||||
),
|
),
|
||||||
|
|||||||
114
skills/recsys-pipeline-architect/SKILL.md
Normal file
114
skills/recsys-pipeline-architect/SKILL.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
---
|
||||||
|
name: recsys-pipeline-architect
|
||||||
|
description: Design composable recommendation, ranking, and feed pipelines using the six-stage Source→Hydrator→Filter→Scorer→Selector→SideEffect framework popularized by xAI's open-sourced For You algorithm. Use this skill whenever the user is building any system that picks "the top K items for a (user, context)" — social feeds, content CMSs, RAG rerankers, task prioritizers, notification triage, search reranking, ad ranking.
|
||||||
|
origin: community
|
||||||
|
---
|
||||||
|
|
||||||
|
# recsys-pipeline-architect
|
||||||
|
|
||||||
|
A spec-and-scaffold skill for building composable recommendation, ranking, and feed pipelines. It encodes the **six-stage pattern** — Source → Hydrator → Filter → Scorer → Selector → SideEffect — popularized by xAI's open-sourced [For You algorithm](https://github.com/xai-org/x-algorithm) (Apache 2.0). This skill is an independent reimplementation of the pattern (MIT) — no code copied from the original.
|
||||||
|
|
||||||
|
Upstream: https://github.com/mturac/recsys-pipeline-architect
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- User wants to build any system that picks "the top K items for a user/context"
|
||||||
|
- User asks "how should I rank X" or describes a feed/personalization problem
|
||||||
|
- User has a scoring function and needs the pipeline plumbing around it
|
||||||
|
- User wants to migrate from a single relevance score to multi-action prediction with tunable weights
|
||||||
|
- User is wrapping an LLM/ML scorer and needs filters, hydrators, side-effects, and a runnable scaffold in their stack (TypeScript / Go / Python)
|
||||||
|
- Triggers: "recommendation system", "feed algorithm", "ranking pipeline", "for you feed", "candidate pipeline", "content recommender", "pipeline architecture for recsys", "RAG retrieval reranker"
|
||||||
|
|
||||||
|
## When NOT to Use
|
||||||
|
|
||||||
|
- Model architecture work (transformer design, two-tower retrieval, embedding training) — this skill is plumbing *around* the model, not the model itself
|
||||||
|
- Pure ML training pipelines — the scoring function is the user's responsibility
|
||||||
|
- Operating a deployed pipeline (monitoring, autoscaling) — out of scope
|
||||||
|
|
||||||
|
## The six-stage framework
|
||||||
|
|
||||||
|
| # | Stage | Job | Parallel? |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **Source** | Fetch candidates from one or more origins | Yes — multiple sources run in parallel |
|
||||||
|
| 2 | **Hydrator** | Enrich each candidate with metadata needed for filtering and scoring | Yes — independent hydrators run in parallel |
|
||||||
|
| 3 | **Filter** | Drop candidates that should never be shown (blocked, expired, duplicate, ineligible) | Sequential — each filter sees fewer items |
|
||||||
|
| 4 | **Scorer** | Assign each surviving candidate one or more scores | Sequential — later scorers see earlier scores |
|
||||||
|
| 5 | **Selector** | Sort by final score, return top K | Single op |
|
||||||
|
| 6 | **SideEffect** | Cache served IDs, log impressions, emit events, update counters | Async — must never block the response |
|
||||||
|
|
||||||
|
### Why this exact order
|
||||||
|
|
||||||
|
- Sources before hydration: know what candidates exist before paying to enrich them
|
||||||
|
- Hydration before filtering: many filters need metadata the source did not provide
|
||||||
|
- Filtering before scoring: scoring is the expensive stage; drop the ineligible first
|
||||||
|
- Scorer chain (not single scorer): real systems compose ML scoring + diversity reranking + business rules
|
||||||
|
- Selector after scoring: keeps scoring deterministic and cacheable
|
||||||
|
- SideEffects last and async: side effects must never block the user response
|
||||||
|
|
||||||
|
## Workflow when invoked
|
||||||
|
|
||||||
|
Walk the user through these eight steps:
|
||||||
|
|
||||||
|
1. **Clarify the use case** (one round, three questions): items being ranked? input context? language/runtime?
|
||||||
|
2. **Identify the candidate sources**: usually in-network (followed/owned/subscribed) + out-of-network (ML retrieval / trending / similar-to-liked)
|
||||||
|
3. **List required hydrations**: for each filter and scorer, what data does it need that the source did not provide?
|
||||||
|
4. **List the filters**: duplicate, self, age, block/mute, previously-served, eligibility. Order matters — cheap before expensive.
|
||||||
|
5. **Design the scorer chain**: primary (ML) → combiner (multi-action with weights) → diversity → business rules
|
||||||
|
6. **Selector**: sort descending by final score, take top K (or stratified mix for in-network/out-of-network)
|
||||||
|
7. **SideEffects**: cache served IDs, emit impression events, update counters, log analytics — all fire-and-forget
|
||||||
|
8. **Generate the scaffold** in the user's stack
|
||||||
|
|
||||||
|
## Key trade-offs to surface (don't default silently)
|
||||||
|
|
||||||
|
### 1. Single score vs multi-action prediction
|
||||||
|
|
||||||
|
- **Single score**: train one model to predict relevance. To change behavior → retrain.
|
||||||
|
- **Multi-action**: predict `P(action)` for many actions (read, like, share, skip, report), combine with weights at serving time. To change behavior → change weights. No retraining.
|
||||||
|
|
||||||
|
The X For You system uses multi-action with both positive and negative weights. Recommend multi-action when the user expects to tune frequently.
|
||||||
|
|
||||||
|
### 2. Candidate isolation in scoring
|
||||||
|
|
||||||
|
- **Isolated**: each candidate scored independently. Deterministic, cacheable.
|
||||||
|
- **Joint**: candidates attend to each other during scoring (e.g., transformer over batch). More expressive but non-deterministic across batches.
|
||||||
|
|
||||||
|
Default to isolation. Joint only when there's a specific reason (e.g., explicit batch-aware diversity).
|
||||||
|
|
||||||
|
### 3. Online vs offline
|
||||||
|
|
||||||
|
- **Request-time (online)**: pipeline runs on each request. Latency budget: 100–300ms. Default.
|
||||||
|
- **Pre-computed (offline batch)**: pipeline runs periodically, results cached. Lower latency, lower freshness.
|
||||||
|
- **Hybrid**: candidate retrieval offline, ranking online.
|
||||||
|
|
||||||
|
## Hard rules
|
||||||
|
|
||||||
|
1. **Do not invent benchmark numbers.** "How much faster?" → "depends on workload, run it yourself."
|
||||||
|
2. **Attribution discipline.** When the pattern is referenced, attribute as "popularized by xAI's open-sourced For You algorithm" / `github.com/xai-org/x-algorithm` (Apache 2.0).
|
||||||
|
3. **No trademark use.** Do not name the user's artifact "X-like" or use "For You" branding. Pattern is free; brand is not. Suggested naming: "candidate pipeline", "feed pipeline", "ranking pipeline", "recsys pipeline".
|
||||||
|
4. **Surface trade-offs.** Multi-action vs single, isolation vs joint, online vs offline — never default silently.
|
||||||
|
5. **The generated scaffold must run.** No pseudocode passing as code.
|
||||||
|
6. **Filter order matters.** Cheap before expensive. Universal before user-specific.
|
||||||
|
7. **Side effects never block.** Wrap in fire-and-forget patterns (goroutines / promises without await / asyncio tasks).
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- Scoring before filtering (wastes compute on candidates that will be dropped anyway)
|
||||||
|
- Synchronous side effects (cache writes / impression emits blocking the response)
|
||||||
|
- A single "relevance" score when the product needs to tune for multiple objectives (engagement vs safety vs diversity vs ads)
|
||||||
|
- Joint scoring as default (non-deterministic, harder to cache, doesn't compose with reranking stages)
|
||||||
|
- Generating pseudocode "for illustration" — the scaffold must actually run
|
||||||
|
|
||||||
|
## Upstream contents
|
||||||
|
|
||||||
|
The upstream repository at https://github.com/mturac/recsys-pipeline-architect ships:
|
||||||
|
|
||||||
|
- Full `SKILL.md` with the complete 8-step workflow
|
||||||
|
- 5 load-on-demand reference docs: interfaces in 4 languages (TS/Go/Python/Rust), multi-action scoring pattern, candidate isolation, filter cookbook (12 patterns), scorer cookbook (weighted sum, MMR, diversity penalty, position debiasing)
|
||||||
|
- 3 runnable example scaffolds, every one green on its test suite:
|
||||||
|
- Strapi v5 plugin (TypeScript / Jest — 3/3 pass)
|
||||||
|
- Zentra-compatible pipeline (Go with generics — 3/3 pass)
|
||||||
|
- PMAI task prioritizer (Python / FastAPI / pytest — 3/3 pass)
|
||||||
|
- v0.1.0 release tagged
|
||||||
|
- MIT license; pattern attributed to xAI X For You algorithm (Apache 2.0)
|
||||||
|
|
||||||
|
Install via skills.sh: `npx skills add mturac/recsys-pipeline-architect`
|
||||||
290
tests/ci/supply-chain-advisory-sources.test.js
Normal file
290
tests/ci/supply-chain-advisory-sources.test.js
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Validate the supply-chain advisory source refresh report.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
const http = require('http');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
const { spawnSync } = require('child_process');
|
||||||
|
|
||||||
|
const SCRIPT_PATH = path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'scripts',
|
||||||
|
'ci',
|
||||||
|
'supply-chain-advisory-sources.js',
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
DEFAULT_ADVISORY_SOURCES,
|
||||||
|
buildAdvisorySourceReport,
|
||||||
|
parseArgs,
|
||||||
|
renderText,
|
||||||
|
} = require(SCRIPT_PATH);
|
||||||
|
|
||||||
|
async function test(name, fn) {
|
||||||
|
try {
|
||||||
|
await fn();
|
||||||
|
console.log(` ✓ ${name}`);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(` ✗ ${name}`);
|
||||||
|
console.log(` Error: ${error.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
console.log('\n=== Testing supply-chain advisory source refresh ===\n');
|
||||||
|
|
||||||
|
let passed = 0;
|
||||||
|
let failed = 0;
|
||||||
|
|
||||||
|
if (await test('default sources cover the active npm and PyPI campaign', async () => {
|
||||||
|
const ids = DEFAULT_ADVISORY_SOURCES.map(source => source.id);
|
||||||
|
for (const requiredId of [
|
||||||
|
'tanstack-postmortem',
|
||||||
|
'github-ghsa-g7cv-rxg3-hmpx',
|
||||||
|
'stepsecurity-mini-shai-hulud',
|
||||||
|
'openai-tanstack-response',
|
||||||
|
'socket-node-ipc',
|
||||||
|
'cisa-npm-compromise',
|
||||||
|
]) {
|
||||||
|
assert.ok(ids.includes(requiredId), `Missing advisory source ${requiredId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ecosystemCoverage = new Set(DEFAULT_ADVISORY_SOURCES.flatMap(source => source.ecosystems));
|
||||||
|
assert.ok(ecosystemCoverage.has('npm'));
|
||||||
|
assert.ok(ecosystemCoverage.has('PyPI'));
|
||||||
|
assert.ok(ecosystemCoverage.has('AI developer tooling'));
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (await test('offline report emits passing coverage checks and Linear-ready ITO-57 payload', async () => {
|
||||||
|
const report = await buildAdvisorySourceReport({
|
||||||
|
generatedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
refresh: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(report.schema_version, 'ecc.supply-chain-advisory-sources.v1');
|
||||||
|
assert.strictEqual(report.ready, true);
|
||||||
|
assert.strictEqual(report.refresh.enabled, false);
|
||||||
|
assert.ok(report.sources.length >= 8);
|
||||||
|
assert.ok(report.checks.every(check => check.status === 'pass'));
|
||||||
|
assert.strictEqual(report.linear.status.issueId, 'ITO-57');
|
||||||
|
assert.match(report.linear.status.summary, /advisory sources current/i);
|
||||||
|
assert.match(report.linear.status.remaining, /Linear status/i);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (await test('refresh mode records per-source live check results', async () => {
|
||||||
|
const calls = [];
|
||||||
|
const report = await buildAdvisorySourceReport({
|
||||||
|
generatedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
refresh: true,
|
||||||
|
fetchSource: async source => {
|
||||||
|
calls.push(source.id);
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
statusCode: 200,
|
||||||
|
finalUrl: source.url,
|
||||||
|
checkedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
calls.sort(),
|
||||||
|
DEFAULT_ADVISORY_SOURCES.filter(source => source.refresh !== false).map(source => source.id).sort(),
|
||||||
|
);
|
||||||
|
assert.strictEqual(report.refresh.enabled, true);
|
||||||
|
assert.strictEqual(report.refresh.ok, true);
|
||||||
|
assert.ok(report.sources.every(source => source.refreshStatus.status === 'ok'));
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (await test('refresh errors are captured as evidence without breaking offline source coverage', async () => {
|
||||||
|
const report = await buildAdvisorySourceReport({
|
||||||
|
generatedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
refresh: true,
|
||||||
|
fetchSource: async source => ({
|
||||||
|
ok: source.id !== 'socket-node-ipc',
|
||||||
|
statusCode: source.id === 'socket-node-ipc' ? 403 : 200,
|
||||||
|
error: source.id === 'socket-node-ipc' ? 'forbidden' : null,
|
||||||
|
finalUrl: source.url,
|
||||||
|
checkedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const socketSource = report.sources.find(source => source.id === 'socket-node-ipc');
|
||||||
|
assert.strictEqual(report.ready, true);
|
||||||
|
assert.strictEqual(report.refresh.ok, false);
|
||||||
|
assert.strictEqual(socketSource.refreshStatus.status, 'warning');
|
||||||
|
assert.match(socketSource.refreshStatus.error, /forbidden/);
|
||||||
|
assert.ok(report.checks.some(check => check.id === 'advisory-refresh' && check.status === 'warn'));
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (await test('CLI JSON can be written as a scheduled workflow artifact', async () => {
|
||||||
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-advisory-sources-'));
|
||||||
|
const outputPath = path.join(tempDir, 'advisory-sources.json');
|
||||||
|
try {
|
||||||
|
const result = spawnSync('node', [
|
||||||
|
SCRIPT_PATH,
|
||||||
|
'--json',
|
||||||
|
'--generated-at',
|
||||||
|
'2026-05-16T00:00:00.000Z',
|
||||||
|
'--write',
|
||||||
|
outputPath,
|
||||||
|
], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
shell: process.platform === 'win32',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(result.status, 0, result.stderr);
|
||||||
|
const parsed = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
|
||||||
|
assert.strictEqual(parsed.schema_version, 'ecc.supply-chain-advisory-sources.v1');
|
||||||
|
assert.strictEqual(parsed.ready, true);
|
||||||
|
assert.ok(parsed.linear.status.evidence.length >= 3);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (await test('argument parser covers strict refresh, timeout validation, and unknown flags', async () => {
|
||||||
|
const parsed = parseArgs(['--strict-refresh', '--timeout-ms', '250', '--json']);
|
||||||
|
assert.strictEqual(parsed.refresh, true);
|
||||||
|
assert.strictEqual(parsed.strictRefresh, true);
|
||||||
|
assert.strictEqual(parsed.timeoutMs, 250);
|
||||||
|
assert.strictEqual(parsed.json, true);
|
||||||
|
|
||||||
|
assert.throws(() => parseArgs(['--timeout-ms', '0']), /positive number/);
|
||||||
|
assert.throws(() => parseArgs(['--write']), /requires a path/);
|
||||||
|
assert.throws(() => parseArgs(['--wat']), /Unknown argument/);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (await test('invalid source coverage fails closed with actionable checks', async () => {
|
||||||
|
const report = await buildAdvisorySourceReport({
|
||||||
|
generatedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
id: 'one-source',
|
||||||
|
title: 'Incomplete source set',
|
||||||
|
publisher: 'Test',
|
||||||
|
url: 'https://example.com',
|
||||||
|
sourceType: 'incident-analysis',
|
||||||
|
ecosystems: ['npm'],
|
||||||
|
signals: ['tanstack'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(report.ready, false);
|
||||||
|
assert.ok(report.checks.some(check => check.id === 'advisory-source-count' && check.status === 'fail'));
|
||||||
|
assert.ok(report.checks.some(check => check.id === 'advisory-ecosystem-coverage' && check.status === 'fail'));
|
||||||
|
assert.ok(report.checks.some(check => check.id === 'advisory-signal-coverage' && check.status === 'fail'));
|
||||||
|
assert.match(report.linear.status.summary, /needs repair/i);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (await test('CLI text output and invalid flag errors are stable', async () => {
|
||||||
|
const help = spawnSync('node', [SCRIPT_PATH, '--help'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
shell: process.platform === 'win32',
|
||||||
|
});
|
||||||
|
assert.strictEqual(help.status, 0);
|
||||||
|
assert.match(help.stdout, /--strict-refresh/);
|
||||||
|
|
||||||
|
const text = spawnSync('node', [
|
||||||
|
SCRIPT_PATH,
|
||||||
|
'--generated-at',
|
||||||
|
'2026-05-16T00:00:00.000Z',
|
||||||
|
], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
shell: process.platform === 'win32',
|
||||||
|
});
|
||||||
|
assert.strictEqual(text.status, 0, text.stderr);
|
||||||
|
assert.match(text.stdout, /Supply-chain advisory sources: ready/);
|
||||||
|
assert.match(text.stdout, /Linear ITO-57:/);
|
||||||
|
|
||||||
|
const invalid = spawnSync('node', [SCRIPT_PATH, '--unknown'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
shell: process.platform === 'win32',
|
||||||
|
});
|
||||||
|
assert.strictEqual(invalid.status, 2);
|
||||||
|
assert.match(invalid.stderr, /Unknown argument/);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (await test('text renderer covers blocked and refresh-warning states', async () => {
|
||||||
|
const blocked = await buildAdvisorySourceReport({
|
||||||
|
generatedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
sources: [],
|
||||||
|
});
|
||||||
|
const blockedText = renderText(blocked);
|
||||||
|
assert.match(blockedText, /blocked/);
|
||||||
|
assert.match(blockedText, /not requested/);
|
||||||
|
|
||||||
|
const warning = await buildAdvisorySourceReport({
|
||||||
|
generatedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
refresh: true,
|
||||||
|
fetchSource: async source => ({
|
||||||
|
ok: source.id !== 'tanstack-postmortem',
|
||||||
|
statusCode: source.id === 'tanstack-postmortem' ? 500 : 200,
|
||||||
|
error: source.id === 'tanstack-postmortem' ? 'server error' : null,
|
||||||
|
checkedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
finalUrl: source.url,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const warningText = renderText(warning);
|
||||||
|
assert.match(warningText, /warnings=1/);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (await test('default refresh follows redirects and retries GET for unsupported HEAD', async () => {
|
||||||
|
const server = http.createServer((request, response) => {
|
||||||
|
if (request.url === '/redirect') {
|
||||||
|
response.writeHead(302, { Location: '/ok' });
|
||||||
|
response.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.url === '/head-unsupported' && request.method === 'HEAD') {
|
||||||
|
response.writeHead(405);
|
||||||
|
response.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||||
|
response.end('ok');
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
||||||
|
const { port } = server.address();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const sources = DEFAULT_ADVISORY_SOURCES.map((source, index) => ({
|
||||||
|
...source,
|
||||||
|
url: index === 0
|
||||||
|
? `http://127.0.0.1:${port}/redirect`
|
||||||
|
: `http://127.0.0.1:${port}/head-unsupported`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const report = await buildAdvisorySourceReport({
|
||||||
|
generatedAt: '2026-05-16T00:00:00.000Z',
|
||||||
|
refresh: true,
|
||||||
|
sources,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(report.ready, true);
|
||||||
|
assert.strictEqual(report.refresh.ok, true);
|
||||||
|
assert.ok(report.sources.every(source => source.refreshStatus.status === 'ok'));
|
||||||
|
} finally {
|
||||||
|
await new Promise(resolve => server.close(resolve));
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
console.log(`\nPassed: ${passed}`);
|
||||||
|
console.log(`Failed: ${failed}`);
|
||||||
|
|
||||||
|
process.exit(failed > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
@@ -58,9 +58,13 @@ function run() {
|
|||||||
if (test('runs IOC fixtures, emits JSON report, and uploads the artifact', () => {
|
if (test('runs IOC fixtures, emits JSON report, and uploads the artifact', () => {
|
||||||
assert.match(source, /node tests\/ci\/scan-supply-chain-iocs\.test\.js/);
|
assert.match(source, /node tests\/ci\/scan-supply-chain-iocs\.test\.js/);
|
||||||
assert.match(source, /node scripts\/ci\/scan-supply-chain-iocs\.js --json > artifacts\/supply-chain-ioc-report\.json/);
|
assert.match(source, /node scripts\/ci\/scan-supply-chain-iocs\.js --json > artifacts\/supply-chain-ioc-report\.json/);
|
||||||
|
assert.match(source, /node tests\/ci\/supply-chain-advisory-sources\.test\.js/);
|
||||||
|
assert.match(source, /node scripts\/ci\/supply-chain-advisory-sources\.js --refresh --json > artifacts\/supply-chain-advisory-sources\.json/);
|
||||||
assert.match(source, /node scripts\/ci\/validate-workflow-security\.js/);
|
assert.match(source, /node scripts\/ci\/validate-workflow-security\.js/);
|
||||||
assert.match(source, /uses: actions\/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a/);
|
assert.match(source, /uses: actions\/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a/);
|
||||||
assert.match(source, /name: supply-chain-ioc-report/);
|
assert.match(source, /name: supply-chain-ioc-report/);
|
||||||
|
assert.match(source, /artifacts\/supply-chain-ioc-report\.json/);
|
||||||
|
assert.match(source, /artifacts\/supply-chain-advisory-sources\.json/);
|
||||||
assert.match(source, /retention-days: 14/);
|
assert.match(source, /retention-days: 14/);
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
|||||||
@@ -131,9 +131,9 @@ function runTests() {
|
|||||||
else failed++;
|
else failed++;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
test('20% remaining contains red blink ANSI code', () => {
|
test('20% remaining contains bold red ANSI code', () => {
|
||||||
const bar = buildContextBar(20);
|
const bar = buildContextBar(20);
|
||||||
assert.ok(bar.includes('\x1b[5;31m'), `Expected red blink ANSI in: ${JSON.stringify(bar)}`);
|
assert.ok(bar.includes('\x1b[1;31m'), `Expected bold red ANSI in: ${JSON.stringify(bar)}`);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
passed++;
|
passed++;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ function buildExpectedPublishPaths(repoRoot) {
|
|||||||
"scripts/ecc.js",
|
"scripts/ecc.js",
|
||||||
"scripts/catalog.js",
|
"scripts/catalog.js",
|
||||||
"scripts/ci/scan-supply-chain-iocs.js",
|
"scripts/ci/scan-supply-chain-iocs.js",
|
||||||
|
"scripts/ci/supply-chain-advisory-sources.js",
|
||||||
"scripts/consult.js",
|
"scripts/consult.js",
|
||||||
"scripts/claw.js",
|
"scripts/claw.js",
|
||||||
"scripts/discussion-audit.js",
|
"scripts/discussion-audit.js",
|
||||||
@@ -56,6 +57,7 @@ function buildExpectedPublishPaths(repoRoot) {
|
|||||||
"scripts/list-installed.js",
|
"scripts/list-installed.js",
|
||||||
"scripts/loop-status.js",
|
"scripts/loop-status.js",
|
||||||
"scripts/observability-readiness.js",
|
"scripts/observability-readiness.js",
|
||||||
|
"scripts/operator-readiness-dashboard.js",
|
||||||
"scripts/platform-audit.js",
|
"scripts/platform-audit.js",
|
||||||
"scripts/skill-create-output.js",
|
"scripts/skill-create-output.js",
|
||||||
"scripts/repair.js",
|
"scripts/repair.js",
|
||||||
@@ -123,8 +125,10 @@ function main() {
|
|||||||
for (const requiredPath of [
|
for (const requiredPath of [
|
||||||
"scripts/catalog.js",
|
"scripts/catalog.js",
|
||||||
"scripts/ci/scan-supply-chain-iocs.js",
|
"scripts/ci/scan-supply-chain-iocs.js",
|
||||||
|
"scripts/ci/supply-chain-advisory-sources.js",
|
||||||
"scripts/consult.js",
|
"scripts/consult.js",
|
||||||
"scripts/discussion-audit.js",
|
"scripts/discussion-audit.js",
|
||||||
|
"scripts/operator-readiness-dashboard.js",
|
||||||
"scripts/work-items.js",
|
"scripts/work-items.js",
|
||||||
"scripts/platform-audit.js",
|
"scripts/platform-audit.js",
|
||||||
".gemini/GEMINI.md",
|
".gemini/GEMINI.md",
|
||||||
|
|||||||
331
tests/scripts/operator-readiness-dashboard.test.js
Normal file
331
tests/scripts/operator-readiness-dashboard.test.js
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
/**
|
||||||
|
* Tests for scripts/operator-readiness-dashboard.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
const { execFileSync, spawnSync } = require('child_process');
|
||||||
|
|
||||||
|
const SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'operator-readiness-dashboard.js');
|
||||||
|
const { buildReport, parseArgs, renderMarkdown, renderText } = require(SCRIPT);
|
||||||
|
|
||||||
|
function createTempDir(prefix) {
|
||||||
|
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup(dirPath) {
|
||||||
|
fs.rmSync(dirPath, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeFile(rootDir, relativePath, content) {
|
||||||
|
const targetPath = path.join(rootDir, relativePath);
|
||||||
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
||||||
|
fs.writeFileSync(targetPath, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
function seedRepo(rootDir, overrides = {}) {
|
||||||
|
const files = {
|
||||||
|
'package.json': JSON.stringify({
|
||||||
|
name: 'everything-claude-code',
|
||||||
|
files: [
|
||||||
|
'scripts/observability-readiness.js',
|
||||||
|
'scripts/operator-readiness-dashboard.js',
|
||||||
|
'scripts/platform-audit.js'
|
||||||
|
],
|
||||||
|
scripts: {
|
||||||
|
'discussion:audit': 'node scripts/discussion-audit.js',
|
||||||
|
'observability:ready': 'node scripts/observability-readiness.js',
|
||||||
|
'operator:dashboard': 'node scripts/operator-readiness-dashboard.js',
|
||||||
|
'platform:audit': 'node scripts/platform-audit.js',
|
||||||
|
'security:ioc-scan': 'node scripts/ci/scan-supply-chain-iocs.js',
|
||||||
|
'security:advisory-sources': 'node scripts/ci/supply-chain-advisory-sources.js'
|
||||||
|
}
|
||||||
|
}, null, 2),
|
||||||
|
'scripts/operator-readiness-dashboard.js': 'operator dashboard generator',
|
||||||
|
'docs/ECC-2.0-GA-ROADMAP.md': [
|
||||||
|
'https://linear.app/itomarkets/project/ecc-platform-roadmap-52b328ee03e1',
|
||||||
|
'Linear ITO-44 ITO-59',
|
||||||
|
'AgentShield PR #89 #78-#89',
|
||||||
|
'AgentShield Enterprise Iteration',
|
||||||
|
'ECC-Tools PR #77',
|
||||||
|
'hosted promotion',
|
||||||
|
'announcementGate',
|
||||||
|
'ITO-55'
|
||||||
|
].join('\n'),
|
||||||
|
'docs/releases/2.0.0-rc.1/publication-readiness.md': 'Claude plugin Codex plugin',
|
||||||
|
'docs/releases/2.0.0-rc.1/naming-and-publication-matrix.md': 'Claude plugin Codex plugin npm package Publication Paths',
|
||||||
|
'docs/releases/2.0.0-rc.1/preview-pack-manifest.md': 'publication-readiness.md release-notes.md quickstart.md',
|
||||||
|
'docs/releases/2.0.0-rc.1/release-notes.md': 'release notes',
|
||||||
|
'docs/releases/2.0.0-rc.1/x-thread.md': 'x thread',
|
||||||
|
'docs/releases/2.0.0-rc.1/linkedin-post.md': 'linkedin post',
|
||||||
|
'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-15.md': [
|
||||||
|
'This dashboard is generated by `npm run operator:dashboard`',
|
||||||
|
'operator:dashboard',
|
||||||
|
'Prompt-To-Artifact Checklist',
|
||||||
|
'Next Work Order',
|
||||||
|
'ITO-44',
|
||||||
|
'ITO-59',
|
||||||
|
'PR queue',
|
||||||
|
'Not complete'
|
||||||
|
].join('\n'),
|
||||||
|
'docs/HERMES-SETUP.md': 'Hermes setup',
|
||||||
|
'skills/hermes-imports/SKILL.md': 'Hermes imports',
|
||||||
|
'docs/stale-pr-salvage-ledger.md': 'Manual review tail',
|
||||||
|
'docs/architecture/progress-sync-contract.md': 'GitHub PRs/issues/discussions Linear project local handoff repo roadmap scripts/work-items.js',
|
||||||
|
'docs/architecture/observability-readiness.md': 'observability-readiness.js',
|
||||||
|
'docs/security/supply-chain-incident-response.md': 'TanStack Mini Shai-Hulud node-ipc scan-supply-chain-iocs.js supply-chain-advisory-sources.js',
|
||||||
|
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-15.md': 'TanStack Mini Shai-Hulud Node IPC follow-up node-ipc IOC scan',
|
||||||
|
'.github/workflows/supply-chain-watch.yml': 'name: Supply-Chain Watch supply-chain-advisory-sources.js supply-chain-advisory-sources.json'
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [relativePath, content] of Object.entries({ ...files, ...overrides })) {
|
||||||
|
if (content === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
writeFile(rootDir, relativePath, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(args = [], options = {}) {
|
||||||
|
return execFileSync('node', [SCRIPT, ...args], {
|
||||||
|
cwd: options.cwd || path.join(__dirname, '..', '..'),
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function runProcess(args = [], options = {}) {
|
||||||
|
return spawnSync('node', [SCRIPT, ...args], {
|
||||||
|
cwd: options.cwd || path.join(__dirname, '..', '..'),
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function test(name, fn) {
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
console.log(` PASS ${name}`);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(` FAIL ${name}`);
|
||||||
|
console.log(` Error: ${error.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTests() {
|
||||||
|
console.log('\n=== Testing operator-readiness-dashboard.js ===\n');
|
||||||
|
|
||||||
|
let passed = 0;
|
||||||
|
let failed = 0;
|
||||||
|
|
||||||
|
if (test('parseArgs accepts dashboard flags and rejects invalid values', () => {
|
||||||
|
const rootDir = createTempDir('operator-dashboard-args-');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = parseArgs([
|
||||||
|
'node',
|
||||||
|
'script',
|
||||||
|
'--format=json',
|
||||||
|
`--root=${rootDir}`,
|
||||||
|
'--skip-github',
|
||||||
|
'--allow-untracked',
|
||||||
|
'docs/drafts/',
|
||||||
|
'--repo',
|
||||||
|
'affaan-m/everything-claude-code',
|
||||||
|
'--generated-at',
|
||||||
|
'2026-05-15T00:00:00.000Z'
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert.strictEqual(parsed.format, 'json');
|
||||||
|
assert.strictEqual(parsed.root, path.resolve(rootDir));
|
||||||
|
assert.strictEqual(parsed.skipGithub, true);
|
||||||
|
assert.deepStrictEqual(parsed.allowUntracked, ['docs/drafts/']);
|
||||||
|
assert.deepStrictEqual(parsed.repos, ['affaan-m/everything-claude-code']);
|
||||||
|
assert.strictEqual(parsed.generatedAt, '2026-05-15T00:00:00.000Z');
|
||||||
|
|
||||||
|
assert.throws(() => parseArgs(['node', 'script', '--format', 'xml']), /Invalid format/);
|
||||||
|
assert.throws(() => parseArgs(['node', 'script', '--write', 'dashboard.md', '--format', 'text']), /--write requires/);
|
||||||
|
assert.throws(() => parseArgs(['node', 'script', '--max-open-prs', 'x']), /Invalid --max-open-prs/);
|
||||||
|
assert.throws(() => parseArgs(['node', 'script', '--unknown']), /Unknown argument/);
|
||||||
|
} finally {
|
||||||
|
cleanup(rootDir);
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('seeded repo emits an objective audit with remaining work', () => {
|
||||||
|
const rootDir = createTempDir('operator-dashboard-report-');
|
||||||
|
|
||||||
|
try {
|
||||||
|
seedRepo(rootDir);
|
||||||
|
const report = buildReport({
|
||||||
|
allowUntracked: [],
|
||||||
|
exitCode: false,
|
||||||
|
format: 'json',
|
||||||
|
generatedAt: '2026-05-15T00:00:00.000Z',
|
||||||
|
help: false,
|
||||||
|
repos: [],
|
||||||
|
root: rootDir,
|
||||||
|
skipGithub: true,
|
||||||
|
thresholds: { maxOpenPrs: 20, maxOpenIssues: 20, maxDirtyFiles: 0 },
|
||||||
|
useEnvGithubToken: false,
|
||||||
|
writePath: null
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(report.schema_version, 'ecc.operator-readiness-dashboard.v1');
|
||||||
|
assert.strictEqual(report.generatedAt, '2026-05-15T00:00:00.000Z');
|
||||||
|
assert.strictEqual(report.dashboardReady, true);
|
||||||
|
assert.strictEqual(report.ready, false);
|
||||||
|
assert.strictEqual(report.publicationReady, false);
|
||||||
|
assert.ok(report.requirements.some(item => item.id === 'completion-dashboard' && item.status === 'complete'));
|
||||||
|
assert.ok(report.requirements.some(item => item.id === 'ecc-tools-next-level' && item.status === 'in_progress'));
|
||||||
|
assert.ok(report.top_actions.some(item => item.id === 'naming-and-plugin-publication'));
|
||||||
|
} finally {
|
||||||
|
cleanup(rootDir);
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('markdown output can be written as the dashboard artifact', () => {
|
||||||
|
const rootDir = createTempDir('operator-dashboard-markdown-');
|
||||||
|
const outputPath = path.join(rootDir, 'artifacts', 'dashboard.md');
|
||||||
|
|
||||||
|
try {
|
||||||
|
seedRepo(rootDir);
|
||||||
|
const stdout = run([
|
||||||
|
'--markdown',
|
||||||
|
'--skip-github',
|
||||||
|
`--root=${rootDir}`,
|
||||||
|
'--generated-at=2026-05-15T00:00:00.000Z',
|
||||||
|
'--write',
|
||||||
|
outputPath
|
||||||
|
], { cwd: rootDir });
|
||||||
|
const written = fs.readFileSync(outputPath, 'utf8');
|
||||||
|
|
||||||
|
assert.strictEqual(stdout, written);
|
||||||
|
assert.ok(written.includes('# ECC Operator Readiness Dashboard'));
|
||||||
|
assert.ok(written.includes('Generated: 2026-05-15T00:00:00.000Z'));
|
||||||
|
assert.ok(written.includes('## Prompt-To-Artifact Checklist'));
|
||||||
|
assert.ok(written.includes('Build ITO-44 completion dashboard into a repeatable command'));
|
||||||
|
assert.ok(written.includes('## Next Work Order'));
|
||||||
|
} finally {
|
||||||
|
cleanup(rootDir);
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('text output renders compact status and top actions', () => {
|
||||||
|
const rootDir = createTempDir('operator-dashboard-text-');
|
||||||
|
|
||||||
|
try {
|
||||||
|
seedRepo(rootDir);
|
||||||
|
const stdout = run([
|
||||||
|
'--format=text',
|
||||||
|
'--skip-github',
|
||||||
|
`--root=${rootDir}`,
|
||||||
|
'--generated-at=2026-05-15T00:00:00.000Z'
|
||||||
|
], { cwd: rootDir });
|
||||||
|
|
||||||
|
assert.ok(stdout.includes('ECC Operator Readiness Dashboard'));
|
||||||
|
assert.ok(stdout.includes('work remaining'));
|
||||||
|
assert.ok(stdout.includes('Dashboard ready: true'));
|
||||||
|
assert.ok(stdout.includes('Publication ready: false'));
|
||||||
|
assert.ok(stdout.includes('Top actions:'));
|
||||||
|
assert.ok(stdout.includes('naming-and-plugin-publication'));
|
||||||
|
} finally {
|
||||||
|
cleanup(rootDir);
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('renderers handle a ready report with no top actions', () => {
|
||||||
|
const report = {
|
||||||
|
dashboardReady: true,
|
||||||
|
generatedAt: '2026-05-15T00:00:00.000Z',
|
||||||
|
head: 'abc123',
|
||||||
|
next_work_order: ['Ship release evidence'],
|
||||||
|
platform: {
|
||||||
|
blockingDirtyCount: 0,
|
||||||
|
discussionsMissingAcceptedAnswer: 0,
|
||||||
|
discussionsNeedingMaintainerTouch: 0,
|
||||||
|
githubSkipped: false,
|
||||||
|
ignoredDirtyCount: 0,
|
||||||
|
openIssues: 1,
|
||||||
|
openPrs: 1,
|
||||||
|
ready: true
|
||||||
|
},
|
||||||
|
publicationReady: true,
|
||||||
|
ready: true,
|
||||||
|
requirements: [
|
||||||
|
{
|
||||||
|
artifact: 'artifact.md',
|
||||||
|
evidence: 'verified',
|
||||||
|
gap: '',
|
||||||
|
id: 'release',
|
||||||
|
requirement: 'Release is approved',
|
||||||
|
status: 'complete'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
top_actions: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const text = renderText(report);
|
||||||
|
assert.ok(text.includes('objective ready'));
|
||||||
|
assert.ok(text.includes('Commit: abc123'));
|
||||||
|
assert.ok(text.includes(' none'));
|
||||||
|
|
||||||
|
const markdown = renderMarkdown(report);
|
||||||
|
assert.ok(markdown.includes('Status: objective ready'));
|
||||||
|
assert.ok(markdown.includes('| PR queue | Current | 1 open PRs across tracked repos |'));
|
||||||
|
assert.ok(markdown.includes('| Publication | Ready |'));
|
||||||
|
assert.ok(markdown.includes('- none'));
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('exit-code mode fails closed while macro objective has gaps', () => {
|
||||||
|
const rootDir = createTempDir('operator-dashboard-exit-');
|
||||||
|
|
||||||
|
try {
|
||||||
|
seedRepo(rootDir);
|
||||||
|
const result = runProcess([
|
||||||
|
'--json',
|
||||||
|
'--skip-github',
|
||||||
|
`--root=${rootDir}`,
|
||||||
|
'--generated-at=2026-05-15T00:00:00.000Z',
|
||||||
|
'--exit-code'
|
||||||
|
], { cwd: rootDir });
|
||||||
|
|
||||||
|
assert.strictEqual(result.status, 2);
|
||||||
|
assert.strictEqual(result.stderr, '');
|
||||||
|
assert.ok(result.stdout.includes('"ready": false'));
|
||||||
|
assert.ok(result.stdout.includes('"publicationReady": false'));
|
||||||
|
} finally {
|
||||||
|
cleanup(rootDir);
|
||||||
|
}
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('cli help exits successfully and invalid cli flags fail before reporting', () => {
|
||||||
|
const help = runProcess(['--help']);
|
||||||
|
assert.strictEqual(help.status, 0);
|
||||||
|
assert.strictEqual(help.stderr, '');
|
||||||
|
assert.ok(help.stdout.includes('Usage: node scripts/operator-readiness-dashboard.js'));
|
||||||
|
assert.ok(help.stdout.includes('--write <path>'));
|
||||||
|
|
||||||
|
const invalid = runProcess(['--format=xml']);
|
||||||
|
assert.strictEqual(invalid.status, 1);
|
||||||
|
assert.strictEqual(invalid.stdout, '');
|
||||||
|
assert.match(invalid.stderr, /Error: Invalid format/);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
console.log(`\nPassed: ${passed}`);
|
||||||
|
console.log(`Failed: ${failed}`);
|
||||||
|
|
||||||
|
if (failed > 0) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
runTests();
|
||||||
|
}
|
||||||
@@ -32,8 +32,10 @@ function seedRepo(rootDir, overrides = {}) {
|
|||||||
scripts: {
|
scripts: {
|
||||||
'platform:audit': 'node scripts/platform-audit.js',
|
'platform:audit': 'node scripts/platform-audit.js',
|
||||||
'discussion:audit': 'node scripts/discussion-audit.js',
|
'discussion:audit': 'node scripts/discussion-audit.js',
|
||||||
|
'operator:dashboard': 'node scripts/operator-readiness-dashboard.js',
|
||||||
'observability:ready': 'node scripts/observability-readiness.js',
|
'observability:ready': 'node scripts/observability-readiness.js',
|
||||||
'security:ioc-scan': 'node scripts/ci/scan-supply-chain-iocs.js',
|
'security:ioc-scan': 'node scripts/ci/scan-supply-chain-iocs.js',
|
||||||
|
'security:advisory-sources': 'node scripts/ci/supply-chain-advisory-sources.js',
|
||||||
'harness:audit': 'node scripts/harness-audit.js'
|
'harness:audit': 'node scripts/harness-audit.js'
|
||||||
}
|
}
|
||||||
}, null, 2),
|
}, null, 2),
|
||||||
@@ -54,7 +56,8 @@ function seedRepo(rootDir, overrides = {}) {
|
|||||||
'TanStack',
|
'TanStack',
|
||||||
'Mini Shai-Hulud',
|
'Mini Shai-Hulud',
|
||||||
'node-ipc',
|
'node-ipc',
|
||||||
'scan-supply-chain-iocs.js'
|
'scan-supply-chain-iocs.js',
|
||||||
|
'supply-chain-advisory-sources.js'
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-15.md': [
|
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-15.md': [
|
||||||
'TanStack',
|
'TanStack',
|
||||||
@@ -64,12 +67,16 @@ function seedRepo(rootDir, overrides = {}) {
|
|||||||
'IOC scan'
|
'IOC scan'
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-15.md': [
|
'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-15.md': [
|
||||||
|
'This dashboard is generated by `npm run operator:dashboard`',
|
||||||
'Prompt-To-Artifact Checklist',
|
'Prompt-To-Artifact Checklist',
|
||||||
'ITO-44',
|
'ITO-44',
|
||||||
'ITO-59',
|
'ITO-59',
|
||||||
'PR queue',
|
'PR queue',
|
||||||
'Not complete'
|
'Not complete',
|
||||||
].join('\n')
|
'operator:dashboard',
|
||||||
|
'Next Work Order'
|
||||||
|
].join('\n'),
|
||||||
|
'scripts/operator-readiness-dashboard.js': 'operator dashboard generator'
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [relativePath, content] of Object.entries({ ...files, ...overrides })) {
|
for (const [relativePath, content] of Object.entries({ ...files, ...overrides })) {
|
||||||
@@ -201,6 +208,7 @@ function runTests() {
|
|||||||
assert.strictEqual(parsed.github.skipped, true);
|
assert.strictEqual(parsed.github.skipped, true);
|
||||||
assert.ok(parsed.checks.some(check => check.id === 'roadmap-linear-mirror' && check.status === 'pass'));
|
assert.ok(parsed.checks.some(check => check.id === 'roadmap-linear-mirror' && check.status === 'pass'));
|
||||||
assert.ok(parsed.checks.some(check => check.id === 'supply-chain-runbook' && check.status === 'pass'));
|
assert.ok(parsed.checks.some(check => check.id === 'supply-chain-runbook' && check.status === 'pass'));
|
||||||
|
assert.ok(parsed.checks.some(check => check.id === 'operator-dashboard-command' && check.status === 'pass'));
|
||||||
assert.ok(parsed.checks.some(check => check.id === 'operator-readiness-dashboard' && check.status === 'pass'));
|
assert.ok(parsed.checks.some(check => check.id === 'operator-readiness-dashboard' && check.status === 'pass'));
|
||||||
assert.deepStrictEqual(parsed.top_actions, []);
|
assert.deepStrictEqual(parsed.top_actions, []);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user