mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-10 18:23:12 +08:00
Compare commits
34 Commits
fix/gategu
...
pr-1903
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c802c33abc | ||
|
|
0e66c838c7 | ||
|
|
cb9702ca99 | ||
|
|
f9384427b8 | ||
|
|
4423f10cfb | ||
|
|
3b12fb273f | ||
|
|
4fb80d8861 | ||
|
|
a27831c13e | ||
|
|
b24d762caa | ||
|
|
f94478e524 | ||
|
|
6cdac19764 | ||
|
|
af3a206412 | ||
|
|
20f00c1410 | ||
|
|
e7a6f137e5 | ||
|
|
7596502092 | ||
|
|
c04baa8c25 | ||
|
|
9082bdedac | ||
|
|
3243a1c5d3 | ||
|
|
69401b28b3 | ||
|
|
9a5ed3223a | ||
|
|
d844bd6bfc | ||
|
|
cf54c791e4 | ||
|
|
bd4369e1d5 | ||
|
|
f2be190dcb | ||
|
|
2afef0f18b | ||
|
|
967e5c6922 | ||
|
|
2d29643dd4 | ||
|
|
c2762dd569 | ||
|
|
cb3509ee19 | ||
|
|
42f04edc03 | ||
|
|
d4728a0d80 | ||
|
|
0e169fecbc | ||
|
|
b2506f82f6 | ||
|
|
f6e13ab520 |
@@ -16,12 +16,14 @@ so the live execution truth is split across:
|
||||
|
||||
As of 2026-05-13:
|
||||
|
||||
- Public 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
|
||||
`ECC-Tools/ECC-website`.
|
||||
- Public GitHub discussions are also clean across those tracked repos:
|
||||
the latest GraphQL sweep found only closed discussions on the trunk
|
||||
discussion surface, and satellite discussion surfaces are disabled or empty.
|
||||
`ECC-Tools/ECC-website`: the latest sweep found 0 open PRs and 0 open
|
||||
issues across all five repos.
|
||||
- GitHub discussions are also clean across those tracked repos:
|
||||
the latest GraphQL sweep found 52 total trunk discussions with 0 open,
|
||||
and 0 total/open discussions on AgentShield, JARVIS, ECC-Tools, and the
|
||||
ECC-Tools website.
|
||||
- The final open public GitHub issue, #1314, was closed as a non-actionable
|
||||
external badge/listing notification with a courtesy comment.
|
||||
- Linear issue creation for this project was re-tested after GitHub cleanup and
|
||||
@@ -30,7 +32,7 @@ As of 2026-05-13:
|
||||
Linear project status updates remain the active tracking surfaces until the
|
||||
workspace is upgraded or issue capacity is freed.
|
||||
- `npm run harness:audit -- --format json` reports 70/70 on current `main`.
|
||||
- `npm run observability:ready` reports 18/18 readiness on current `main`,
|
||||
- `npm run observability:ready` reports 21/21 readiness on current `main`,
|
||||
including the GitHub/Linear/handoff/roadmap progress-sync contract.
|
||||
- PR #1846 merged as `797f283036904128bb1b348ae62019eb9f08cf39` and made
|
||||
npm registry signature verification a durable workflow-security gate:
|
||||
@@ -39,6 +41,165 @@ As of 2026-05-13:
|
||||
`docs/security/supply-chain-incident-response.md`, plus a workflow-security
|
||||
validator rule blocking `pull_request_target` workflows from restoring or
|
||||
saving shared dependency caches.
|
||||
- PR #1850 merged as `248673271455e9dc85b8add2a6ab76107b718639` and removed
|
||||
shell access from read-only analyzer agents and zh-CN copies, reducing
|
||||
AgentShield high findings on that surface without changing operator agents.
|
||||
- PR #1851 merged as `209abd403b7eaa968c6d4fa67be82e04b55706d6` and made
|
||||
`persist-credentials: false` mandatory for `actions/checkout` in workflows
|
||||
with write permissions.
|
||||
- PR #1860 merged as `c2762dd5691a33aaa7f84a0a4901a5bab7980fc8` and closed
|
||||
#1859 by adding the Ruby/Rails language pack surface, install aliases,
|
||||
selective-install components, and focused install-manifest executor tests.
|
||||
- AgentShield PR #78 merged as `1b19a985d6ae1346244089a78806a7d5eaaf270e`
|
||||
and hardened the release workflow with `persist-credentials: false` plus
|
||||
`npm ci --ignore-scripts` in the write/id-token release path.
|
||||
- AgentShield PR #79 merged as `86a823c5f2c35ee97e6ecf6f99e9ac301d54119a`
|
||||
and moved baseline/watch/remediation fingerprints to a shared hashed
|
||||
evidence fingerprint helper. New baselines omit raw finding evidence while
|
||||
older raw-evidence baselines remain comparable.
|
||||
- AgentShield PR #80 merged as `8ed379d1de067b25640ac6273aa4d9f8e6735d43`
|
||||
and added prioritized corpus accuracy recommendations to failed corpus gates,
|
||||
mapping misses by category, missing rule, and config ID so enterprise
|
||||
scanner-regression work has an actionable improvement plan.
|
||||
- AgentShield PR #81 merged as `6583884e74ba2e896942113e1ce3146230e6fb76`
|
||||
and added ordered remediation workflow phases to remediation plans, routing
|
||||
safe auto-fixes, manual review, and verification through stable finding
|
||||
fingerprints without copying raw evidence.
|
||||
- AgentShield PR #82 merged as `51336ba074ad5e9fed2c0aa3237422be22147e76`
|
||||
and expanded the built-in attack corpus with an env proxy hijack scenario
|
||||
covering proxy/runtime mutation, env-token exfiltration, DNS exfiltration,
|
||||
credential-store access, and clipboard access.
|
||||
- JARVIS PR #13 merged as `127efabbfb5033ae53d7a53e1546aa3c33d6f962`
|
||||
and hardened CI/deploy workflows with npm registry signature verification,
|
||||
disabled persisted checkout credentials in write-permission jobs, and pinned
|
||||
the Vercel CLI install instead of using `latest`.
|
||||
- ECC-Tools PR #53 merged as `99018e943d03f024de8c9d278c91f66393d4f1ee`
|
||||
and added npm registry signature verification before the existing production
|
||||
dependency audit in CI.
|
||||
- ECC-Tools PR #54 merged as `05df89721f49c1e19d8502c545e26f5694806998`
|
||||
and made `/ecc-tools followups sync-linear` track copy-ready PR drafts in
|
||||
the Linear/project backlog when `open-pr-drafts` is not used, preserving
|
||||
useful stale-PR salvage work without opening extra PR shells.
|
||||
- ECC-Tools PR #55 merged as `5d8c112cce4794cfa089d5b0ea661ba87a178be1`
|
||||
and added analysis-depth readiness to `/ecc-tools analyze` comments,
|
||||
separating commit-history-only repos from evidence-backed and deep-ready repos
|
||||
using CI/CD, security, harness, reference/eval, AI routing/cost-control, and
|
||||
team handoff evidence.
|
||||
- ECC-Tools PR #56 merged as `5b729c88641eafe80f65364bab3fc74d0270f57b`
|
||||
and added the authenticated `/api/analysis/depth-plan` contract that maps
|
||||
analysis-depth readiness into concrete hosted jobs for CI diagnostics,
|
||||
security evidence review, harness compatibility, reference-set evaluation,
|
||||
AI routing/cost review, and team backlog routing.
|
||||
- ECC-Tools PR #57 merged as `4cc61112a4cc9feec7b07af09321f360e34af6a4`
|
||||
and added the first executable hosted analysis job:
|
||||
`/api/analysis/jobs/ci-diagnostics` now gates on CI/CD readiness, inspects
|
||||
workflow/test-runner/failure-evidence artifacts, returns CI hardening
|
||||
findings and next actions, and charges usage only after successful execution.
|
||||
- ECC-Tools PR #58 merged as `ce09dd8d9b46f65c6b88dc4f48cfb6b6227ae0bf`
|
||||
and added the second executable hosted analysis job:
|
||||
`/api/analysis/jobs/security-evidence-review` now gates on security-evidence
|
||||
readiness, inspects capped AgentShield evidence-pack, policy, baseline,
|
||||
SBOM, SARIF, and security-scan artifacts, returns supply-chain evidence
|
||||
findings and next actions, and charges usage only after successful execution.
|
||||
- ECC-Tools PR #59 merged as `505b372dbd8f75f996d9e2ed079effd30cec5ba5`
|
||||
and added the third executable hosted analysis job:
|
||||
`/api/analysis/jobs/harness-compatibility-audit` now gates on harness-config
|
||||
readiness, inspects capped Claude, Codex, OpenCode, MCP, plugin, and
|
||||
cross-harness documentation artifacts, excludes local secret-bearing config
|
||||
paths from fetches, returns portability findings and next actions, and
|
||||
charges usage only after successful execution.
|
||||
- ECC-Tools PR #60 merged as `b75e0a49ba5672b1ec9a2a4880ddcfa2d07dc557`
|
||||
and added the fourth executable hosted analysis job:
|
||||
`/api/analysis/jobs/reference-set-evaluation` now gates on reference-evidence
|
||||
readiness, evaluates analyzer corpus, RAG/evaluator, PR salvage/review,
|
||||
harness, security, and CI failure-mode evidence, excludes obvious
|
||||
secret-bearing fixture paths from fetches, returns reference coverage
|
||||
findings and next actions, and charges usage only after successful execution.
|
||||
- ECC-Tools PR #61 merged as `7b01b67cae0b80774b311cb515b7eca0aa038c65`
|
||||
and added the fifth executable hosted analysis job:
|
||||
`/api/analysis/jobs/ai-routing-cost-review` now gates on AI routing/cost
|
||||
readiness, evaluates model routing, token budget, usage-limit, rate-limit,
|
||||
billing/entitlement, cost-regression, and cost-policy evidence, excludes
|
||||
obvious secret-bearing paths from fetches, returns cost-control findings and
|
||||
next actions, and charges usage only after successful execution.
|
||||
- ECC-Tools PR #62 merged as `781d6733e56f7556edb43fb96bdfb00b1f0a3aa6`
|
||||
and added the sixth executable hosted analysis job:
|
||||
`/api/analysis/jobs/team-backlog-routing` now gates on team handoff/project
|
||||
tracking readiness, evaluates roadmap, runbook, handoff, release-plan,
|
||||
issue-template, ownership, project-tracker, backlog, and follow-up evidence,
|
||||
excludes obvious secret-bearing paths from fetches, returns team-routing
|
||||
findings and next actions, and charges usage only after successful execution.
|
||||
- ECC-Tools PR #63 merged as `fb9e4c5ceb9ccde50da74c7a69c3fa4bd321fc07`
|
||||
and made the hosted execution plan operator-visible on queued PR analysis:
|
||||
the queue now publishes a non-blocking `ECC Tools / Hosted Depth Plan`
|
||||
check-run on the PR head SHA with ready/blocked hosted executor commands
|
||||
and next action text, while keeping check-run publication best-effort so
|
||||
bundle generation and analysis comments are not blocked.
|
||||
- ECC-Tools PR #64 merged as `72020ef94db94840812977ea7ac37e9344036668`
|
||||
and added PR-facing hosted job dispatch controls:
|
||||
`/ecc-tools analyze --job ...` comments now queue hosted jobs against the
|
||||
PR head SHA, execute them through the existing hosted readiness/evidence
|
||||
gates, post artifacts/findings/next actions back to the PR, and scope
|
||||
idempotency keys by job id so hosted jobs do not collide with bundle
|
||||
analysis.
|
||||
- ECC-Tools PR #65 merged as `bacd4adf6a3a629e8d403865456d15f127baaf4e`
|
||||
and added hosted job result history/check-run summaries:
|
||||
queued hosted jobs now cache both the latest result and immutable run records
|
||||
for completed or blocked runs, then publish a non-blocking per-job check-run
|
||||
on the PR head SHA with artifacts, findings, readiness blockers, and next
|
||||
actions.
|
||||
- ECC-Tools PR #66 merged as `4e1db48252d068ea5dcf4308b0bc11b0dfe0c9ce`
|
||||
and added a read-only hosted status command:
|
||||
`/ecc-tools analyze --job status` now reads the #65 latest-result cache for
|
||||
the current PR head and posts a compact completed/blocked/not-run table with
|
||||
the next hosted job command, without queueing work or billing usage.
|
||||
- ECC-Tools PR #67 merged as `f20e6bec2b0bf49e4cc36e08b7285c795973b73d`
|
||||
and made the hosted depth-plan check-run status-aware:
|
||||
queued PR analysis now reads the #65/#66 latest-result cache when publishing
|
||||
`ECC Tools / Hosted Depth Plan`, includes the latest hosted run status in
|
||||
the plan table, and recommends the next unrun ready job before reruns.
|
||||
- ECC-Tools PR #68 merged as `2cde524b5ef8f34ab7bb1af973248fe4be4359f8`
|
||||
and added deterministic hosted promotion readiness:
|
||||
opened/synchronized PRs now publish a non-blocking
|
||||
`ECC Tools / Hosted Promotion Readiness` check-run that compares changed
|
||||
files against the checked-in evaluator/RAG corpus, warns on missing
|
||||
hosted-job promotion evidence, and can be disabled with
|
||||
`PR_HOSTED_PROMOTION_READINESS_CHECK_MODE=off`.
|
||||
- ECC-Tools PR #69 merged as `d0112dac7cef807ae27def41f057682ef0772cce`
|
||||
and extended hosted promotion readiness with deterministic output scoring:
|
||||
the check now reads cached completed hosted job results for the current PR
|
||||
head, scores their artifacts and findings against evaluator/RAG corpus
|
||||
expectations, and treats matching hosted artifacts as promotion evidence
|
||||
before reporting a gap.
|
||||
- ECC-Tools PR #70 merged as `7001d805ac981fe220b4575159f469fbea9dbb76`
|
||||
and added retrieval planning for hosted promotion:
|
||||
the check now emits ranked retrieval candidates from cached hosted artifacts,
|
||||
hosted findings, expected evidence paths, and changed source paths, plus a
|
||||
model prompt seed that tells the later hosted judge not to promote from
|
||||
changed paths alone.
|
||||
- ECC-Tools PR #71 merged as `d41e59ff00fe1bd0b0c96386e56bc5269d7b9c15`
|
||||
and added the first model-backed hosted promotion judge contract:
|
||||
the check now emits a provider-neutral `hosted-promotion-judge.v1` request
|
||||
contract and fails closed unless hosted retrieval evidence, entitlement,
|
||||
remaining budget, and provider configuration are present. It still does not
|
||||
make live model calls.
|
||||
- ECC-Tools PR #72 merged as `973bc51e5436dd279ae5a890cce9811485eef0b5`
|
||||
and executes the hosted promotion model judge behind explicit gates:
|
||||
`PR_HOSTED_PROMOTION_MODEL_JUDGE_MODE=execute` now calls the configured
|
||||
provider only after hosted retrieval evidence, entitlement, budget, provider,
|
||||
and executor gates pass; the check remains non-blocking, strict-JSON-only,
|
||||
and rejects uncited or non-hosted model output without echoing raw responses.
|
||||
- Handoff `ecc-supply-chain-audit-20260513-0645.md` under
|
||||
`~/.cluster-swarm/handoffs/`
|
||||
records the May 13 supply-chain sweep: no active lockfile/manifest hit for
|
||||
TanStack/Mini Shai-Hulud indicators; npm audit/signature checks clean across
|
||||
active npm lockfiles; `cargo audit` clean for `ecc2`; trunk `pip-audit`
|
||||
clean; JARVIS backend pinned-graph Python audit clean under the supported
|
||||
Python 3.12 target.
|
||||
- PR #1861 validation refreshed `node scripts/harness-audit.js --format json`
|
||||
at 70/70 and `npm run observability:ready` at 21/21.
|
||||
- PR #1862 updated this roadmap after the JARVIS backend Python audit was
|
||||
re-run against the supported Python 3.12 pinned graph.
|
||||
- `docs/architecture/harness-adapter-compliance.md` maps Claude Code, Codex,
|
||||
OpenCode, Cursor, Gemini, Zed-adjacent, dmux, Orca, Superset, Ghast, and
|
||||
terminal-only support to install paths, verification commands, and risk
|
||||
@@ -61,8 +222,12 @@ As of 2026-05-13:
|
||||
release-readiness evidence refresh: 70/70 harness audit, adapter compliance
|
||||
PASS, 16/16 observability readiness, 2376/2376 root Node tests, markdownlint,
|
||||
release-surface and npm publish-surface tests, and 462/462 `ecc2` Rust tests.
|
||||
- After #1848, `node tests/run-all.js` reports 2377/2377 and the current
|
||||
observability gate reports 18/18.
|
||||
- `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-13-post-hardening.md`
|
||||
records the post-hardening release-readiness refresh after PR #1850 and
|
||||
PR #1851: 70/70 harness audit, adapter compliance PASS, 18/18 observability
|
||||
readiness, 2380/2380 root Node tests, markdownlint, release-surface and
|
||||
npm publish-surface tests, 462/462 `ecc2` Rust tests, npm audit/signature
|
||||
checks, Rust advisory audit, and TanStack/Mini Shai-Hulud IOC checks.
|
||||
- A detached clean worktree at
|
||||
`bfacf37715b39655cbc2c48f12f2a35c67cb0253` verified Claude plugin tag
|
||||
dry-run without `--force`, local marketplace discovery, temp-home local
|
||||
@@ -121,6 +286,22 @@ As of 2026-05-13:
|
||||
CLI command with severity filtering, JSON metadata output, README/API docs,
|
||||
rebuilt CLI bundle, local TDD coverage, and green remote action/self-scan/Node
|
||||
verification.
|
||||
- AgentShield PR #65 pinned workflow actions for release/security CI hardening.
|
||||
- AgentShield PR #66 disabled cache use in the release publish job so release
|
||||
publication does not depend on mutable restored build state.
|
||||
- AgentShield PR #67 added the first portable enterprise evidence-pack bundle:
|
||||
`agentshield scan --evidence-pack <dir>` writes deterministic manifest,
|
||||
README, JSON, HTML, SARIF, policy-evaluation, baseline-comparison, and
|
||||
supply-chain artifacts with default redaction and `not-run` markers for
|
||||
optional policy/baseline evidence.
|
||||
- AgentShield PR #68 hardened evidence-pack redaction for enterprise credential
|
||||
families including GitHub fine-grained PATs, GitLab PATs, npm tokens, Linear
|
||||
API keys, Stripe keys, Google API keys, Hugging Face tokens, Vercel tokens,
|
||||
AWS access key IDs, and JWT-shaped credentials.
|
||||
- AgentShield PR #69 added the deterministic harness adapter registry. Scan
|
||||
reports now surface local marker evidence for Claude Code, OpenCode, Codex,
|
||||
Gemini, dmux, generic terminal agents, and project-local templates in JSON,
|
||||
markdown, terminal, and HTML outputs.
|
||||
- AgentShield PDF-export decision: defer a native PDF writer for now. The
|
||||
self-contained HTML executive report remains the exportable buyer artifact
|
||||
and can be printed to PDF when needed; native PDF generation should wait for
|
||||
@@ -183,6 +364,74 @@ As of 2026-05-13:
|
||||
quality, AgentShield policy exceptions, skill-quality evidence,
|
||||
deep-analyzer evidence, and RAG/evaluator comparison evidence, with each
|
||||
scenario exercising missing-evidence and evidence-backed diffs.
|
||||
- ECC-Tools PR #41 hardened supply-chain dependencies.
|
||||
- ECC-Tools PR #42 added AgentShield evidence-pack gap prediction and routed
|
||||
missing policy/baseline/allowlist/suppression/supply-chain evidence into the
|
||||
PR-risk taxonomy, follow-up drafts, and Linear-ready backlog table.
|
||||
- ECC-Tools PR #43 recognized the concrete AgentShield #67 evidence-pack
|
||||
artifact contract so canonical bundle files now satisfy the taxonomy and
|
||||
generated follow-up PRs point maintainers at
|
||||
`agentshield scan --evidence-pack <dir>`.
|
||||
- ECC-Tools PR #55 added the first hosted/deeper-analysis readiness signal:
|
||||
analysis comments now classify a repo as commit-history-only,
|
||||
evidence-backed, or deep-ready before routing work into CI, AgentShield,
|
||||
harness, reference-set, RAG/evaluator, AI-routing, cost-control, and
|
||||
Linear/project-tracking lanes.
|
||||
- ECC-Tools PR #56 turned that signal into a hosted execution-plan contract:
|
||||
`/api/analysis/depth-plan` returns ready/blocked jobs and next action text
|
||||
without charging analysis usage or creating bundle PRs.
|
||||
- ECC-Tools PR #57 implemented the first job-specific hosted executor:
|
||||
`/api/analysis/jobs/ci-diagnostics` reuses the depth-readiness gate, internal
|
||||
API auth, installation ownership, repo-access billing checks, capped workflow
|
||||
file reads, and usage accounting to return concrete CI hardening findings.
|
||||
- ECC-Tools PR #58 implemented the second job-specific hosted executor:
|
||||
`/api/analysis/jobs/security-evidence-review` applies the same hosted gates
|
||||
to AgentShield evidence-pack, policy, baseline, SBOM, SARIF, and security
|
||||
scanner artifacts.
|
||||
- ECC-Tools PR #59 implemented the third job-specific hosted executor:
|
||||
`/api/analysis/jobs/harness-compatibility-audit` applies the same hosted
|
||||
gates to Claude, Codex, OpenCode, MCP, plugin, and cross-harness evidence
|
||||
while avoiding local secret-bearing harness config fetches.
|
||||
- ECC-Tools PR #60 implemented the fourth job-specific hosted executor:
|
||||
`/api/analysis/jobs/reference-set-evaluation` applies the same hosted gates
|
||||
to analyzer corpus, RAG/evaluator, PR salvage, harness, security, and CI
|
||||
failure-mode reference evidence while avoiding obvious secret-bearing fixture
|
||||
fetches.
|
||||
- ECC-Tools PR #61 implemented the fifth job-specific hosted executor:
|
||||
`/api/analysis/jobs/ai-routing-cost-review` applies the same hosted gates to
|
||||
model-routing, token-budget, usage-limit, rate-limit, billing/entitlement,
|
||||
cost-regression, and cost-policy evidence while avoiding obvious
|
||||
secret-bearing path fetches.
|
||||
- ECC-Tools PR #62 implemented the sixth job-specific hosted executor:
|
||||
`/api/analysis/jobs/team-backlog-routing` applies the same hosted gates to
|
||||
roadmap, runbook, handoff, release-plan, issue-template, ownership,
|
||||
project-tracker, backlog, and follow-up evidence while avoiding obvious
|
||||
secret-bearing path fetches.
|
||||
- ECC-Tools PR #63 publishes the hosted depth-plan check-run after queued PR
|
||||
analysis completes, making the six hosted executor commands visible on the
|
||||
PR head SHA without turning the check into a merge blocker.
|
||||
- ECC-Tools PR #64 wires those commands into the queue: maintainers can comment
|
||||
`/ecc-tools analyze --job ci-diagnostics`, `security-evidence`,
|
||||
`harness-compatibility`, `reference-set-evaluation`, `ai-routing-cost`, or
|
||||
`team-backlog` on a PR and receive hosted job results in a PR comment.
|
||||
- ECC-Tools PR #65 persists completed and blocked hosted job results to the
|
||||
analysis cache for 30 days and publishes non-blocking `ECC Tools / Hosted
|
||||
Job: ...` check-runs so maintainers can scan hosted outcomes from the PR
|
||||
checks surface instead of rereading older comments.
|
||||
- ECC-Tools PR #66 exposes the cached results from PR comments with
|
||||
`/ecc-tools analyze --job status`, summarizing completed, blocked, and
|
||||
not-yet-run hosted jobs for the PR head and recommending the next hosted job
|
||||
command.
|
||||
- ECC-Tools PR #67 feeds those cached results back into the hosted depth-plan
|
||||
check-run so queued analysis recommends the next unrun ready hosted job from
|
||||
cache state instead of repeating the static readiness order.
|
||||
- ECC-Tools PR #68 adds the first evaluator-backed hosted promotion gate:
|
||||
opened/synchronized PRs get a non-blocking Hosted Promotion Readiness
|
||||
check-run that turns the evaluator/RAG corpus into warnings when changed
|
||||
files match fixture scenarios without their expected evidence artifacts.
|
||||
- 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
|
||||
evidence expectations before the check reports a promotion gap.
|
||||
- ECC PR #1803 landed the contributor Quarkus handling branch after maintainer
|
||||
cleanup, current-`main` alignment, full local validation, and preservation of
|
||||
the author's removal of incomplete ja-JP and zh-CN Quarkus translations.
|
||||
@@ -208,7 +457,7 @@ As of 2026-05-13:
|
||||
|
||||
- Keep public PRs and issues below 20, with zero as the preferred release-lane
|
||||
target.
|
||||
- Maintain 70/70 harness audit and 16/16 observability readiness after every
|
||||
- Maintain 70/70 harness audit and 21/21 observability readiness after every
|
||||
GA-readiness batch.
|
||||
- Do not publish release or social announcements until the GitHub release,
|
||||
npm/package state, billing state, and plugin submission surfaces are verified
|
||||
@@ -225,24 +474,24 @@ is not complete unless the evidence column exists and has been freshly verified.
|
||||
|
||||
| Prompt requirement | Required artifact or gate | Current evidence | Status |
|
||||
| --- | --- | --- | --- |
|
||||
| Keep public PRs below 20 | Repo-family PR recheck | 0 open PRs across the tracked public repos on 2026-05-13 after merging #1848 | Complete for this checkpoint |
|
||||
| Keep public issues below 20 | Repo-family issue recheck | 0 open issues across the tracked public repos on 2026-05-13 | Complete for this checkpoint |
|
||||
| Manage repository discussions | Repo-family discussion recheck | Latest trunk discussion GraphQL sweep returned closed discussions only; satellite repos remain disabled or empty | Complete for this checkpoint |
|
||||
| Manage PR discussions | PR review/comment closure plus merge/close state | #1848 merged after current-head CI; no open PRs remain | Complete for this checkpoint |
|
||||
| Keep public PRs below 20 | Repo-family PR recheck | 0 open PRs across `everything-claude-code`, AgentShield, JARVIS, `ECC-Tools/ECC-Tools`, and `ECC-Tools/ECC-website` on 2026-05-13 after merging ECC #1860, AgentShield #78, JARVIS #13, and ECC-Tools #53 | Complete |
|
||||
| Keep public issues below 20 | Repo-family issue recheck | 0 open issues across `everything-claude-code`, AgentShield, JARVIS, `ECC-Tools/ECC-Tools`, and `ECC-Tools/ECC-website` on 2026-05-13 | Complete |
|
||||
| Manage repository discussions | Repo-family discussion recheck | GraphQL sweep returned 52 total trunk discussions with 0 open; AgentShield, JARVIS, ECC-Tools, and ECC-Tools website returned 0 total/open discussions | Complete |
|
||||
| Manage PR discussions | PR review/comment closure plus merge/close state | ECC #1860, AgentShield #78, JARVIS #13, and ECC-Tools #53/#54 merged after current-head CI/builds; no open tracked PRs remain | Complete |
|
||||
| Salvage useful stale work | `docs/stale-pr-salvage-ledger.md` | Ledger records salvaged, superseded, skipped, and manual-review tails; #1815-#1818 added cost tracking, skill scout, frontend design guidance, code-reviewer false-positive guardrails, and the May 12 gap pass | Complete except translation/manual review tail |
|
||||
| ECC 2.0 preview pack ready | Release docs, quickstart, publication readiness, release notes | `docs/releases/2.0.0-rc.1/` and readiness docs are in-tree; May 13 evidence refresh records harness, adapter, observability, Node, lint, release-surface, npm publish-surface, and Rust checks | Needs final clean-checkout release approval |
|
||||
| Hermes specialized skills included safely | Hermes setup/import docs and sanitized skill surface | Hermes setup and import playbook are public; secrets stay local | Needs final release review |
|
||||
| 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 |
|
||||
| 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, enterprise research roadmap | PRs #53, #55-#64 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` selects baseline drift as the first control-plane slice | Baseline-drift Action and CLI write surfaces landed; evidence-pack routing remains |
|
||||
| ECC Tools next-level app | Billing audit, PR checks, deep analyzer, sync backlog, evaluator/RAG corpus | PRs #26-#40 landed with test evidence | Needs capacity-backed Linear rollout |
|
||||
| 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, and PR Review/Salvage Evidence; #1846 added npm registry signature gates; #1848 added the supply-chain incident-response playbook and `pull_request_target` cache-poisoning validator guard | Partially complete |
|
||||
| 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 | Local corpus complete; hosted integration remains future |
|
||||
| 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 | Needs recurring status updates after each merge batch |
|
||||
| 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 | PRs #53, #55-#64, #67-#69, and #78-#82 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, and env proxy hijack corpus slices landed | Next hosted evidence-pack workflow depth |
|
||||
| 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 | PRs #26-#43 plus #53-#72 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, and opt-in live model-judge execution behind hosted evidence, entitlement, budget, provider, executor, strict JSON, and citation gates | Next work is hosted promotion telemetry and operator review UX |
|
||||
| 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 |
|
||||
| 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 13 sync adds ECC #1860, AgentShield #78-#82, JARVIS #13, ECC-Tools #53-#72, 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 |
|
||||
| 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; `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 18/18 | Complete for local gate |
|
||||
| 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 |
|
||||
| Proper release and notifications | Release tag, npm publish state, plugin state, social posts | Publication readiness gate exists with May 12 dry-run and May 13 readiness evidence | Not complete; approval/live URLs required |
|
||||
|
||||
## Execution Lanes And Tracking Contract
|
||||
@@ -258,9 +507,9 @@ repo evidence and merge commits.
|
||||
| Queue hygiene and salvage | GitHub PR/issue state, salvage ledger | Append ledger entries for any future stale closures | Every cleanup batch |
|
||||
| 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 |
|
||||
| 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 | Hosted retrieval/check-run automation plan |
|
||||
| AgentShield enterprise | AgentShield PR evidence and roadmap notes | Baseline-drift evidence-pack and backlog sync follow-up | Next implementation batch |
|
||||
| ECC Tools app | ECC-Tools PR evidence, billing audit, risk taxonomy, evaluator/RAG corpus | Capacity-backed Linear rollout | Next implementation batch |
|
||||
| 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 |
|
||||
| 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, and #72 adds opt-in live model-judge execution behind hosted-evidence and strict JSON/citation gates | Next implementation batch |
|
||||
| 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 |
|
||||
|
||||
The project status update should always include:
|
||||
@@ -331,7 +580,7 @@ Target: 2026-06-07
|
||||
|
||||
Acceptance:
|
||||
|
||||
- Observability readiness remains 16/16 and is backed by JSONL traces, status
|
||||
- Observability readiness remains 21/21 and is backed by JSONL traces, status
|
||||
snapshots, risk ledger, and exportable handoff contracts.
|
||||
- HUD/status model covers context, tool calls, active agents, todos, checks,
|
||||
cost, risk, and queue state.
|
||||
@@ -437,6 +686,9 @@ Acceptance:
|
||||
- Linear sync maps deferred backlog findings to Linear issues without flooding
|
||||
GitHub, creates or reuses exact-title Linear issues when configured, and
|
||||
reports skipped sync when credentials or team configuration are absent.
|
||||
- Linear/project backlog sync includes copy-ready PR drafts when
|
||||
`/ecc-tools followups sync-linear` is used without `open-pr-drafts`, so
|
||||
stale-PR salvage work remains tracked without opening extra PR shells.
|
||||
- Follow-up generation caps automatic GitHub object creation and keeps overflow
|
||||
findings in a copy-ready project sync backlog.
|
||||
|
||||
@@ -460,14 +712,26 @@ Acceptance:
|
||||
|
||||
## Next Engineering Slices
|
||||
|
||||
1. Finish the AgentShield baseline-drift control-plane slice from
|
||||
1. Continue the AgentShield enterprise control-plane sequence from
|
||||
`docs/architecture/agentshield-enterprise-research-roadmap.md`: PR #63
|
||||
shipped the GitHub Action baseline outputs and job-summary evidence; PR #64
|
||||
shipped GitHub Action baseline outputs and job-summary evidence; PR #64
|
||||
shipped first-class baseline snapshot creation through
|
||||
`agentshield baseline write`; the remaining work is evidence-pack routing
|
||||
and ECC-Tools backlog sync integration.
|
||||
2. Enable/configure the merged Linear backlog sync path after workspace issue
|
||||
capacity clears or the Linear workspace is upgraded.
|
||||
3. Use the ECC-Tools evaluator/RAG corpus as the promotion gate before adding
|
||||
hosted retrieval, vector storage, model-backed judging, or automated
|
||||
`agentshield baseline write`; PR #67 shipped the evidence-pack bundle; PR
|
||||
#68 hardened evidence-pack redaction; PR #69 shipped the multi-harness
|
||||
adapter registry; PR #78 hardened the release workflow for the current
|
||||
supply-chain incident class; PR #79 moved baseline/watch/remediation
|
||||
fingerprints to hashed evidence and stopped writing raw evidence into new
|
||||
baselines; PR #80 added prioritized corpus accuracy recommendations for
|
||||
failed regression gates; PR #81 added ordered remediation workflow phases;
|
||||
PR #82 expanded corpus coverage for env proxy hijacks and out-of-band
|
||||
exfiltration; and ECC-Tools PRs #42/#43 now route and recognize evidence
|
||||
packs. The next slice is hosted evidence-pack workflow depth.
|
||||
2. Add hosted promotion telemetry and operator review UX on top of the #72
|
||||
gated model execution path so live judgments can be audited before any
|
||||
promotion policy becomes enforceable.
|
||||
3. Enable/configure the merged Linear backlog sync path after workspace issue
|
||||
capacity clears or the Linear workspace is upgraded, then verify PR-draft
|
||||
salvage items land in the expected project.
|
||||
4. Use the ECC-Tools evaluator/RAG corpus as the promotion gate before adding
|
||||
hosted retrieval, vector storage, live model-backed judging, or automated
|
||||
check-run promotion.
|
||||
|
||||
@@ -35,6 +35,10 @@ operator needs.
|
||||
- Progress sync: `docs/architecture/progress-sync-contract.md` defines how
|
||||
GitHub, Linear, local handoffs, the repo roadmap, and `scripts/work-items.js`
|
||||
stay aligned during merge batches and release-gate reviews.
|
||||
- Release safety: `docs/releases/2.0.0-rc.1/publication-readiness.md`,
|
||||
post-hardening evidence, supply-chain incident response, workflow-security
|
||||
validation, npm pack checks, and release-surface tests must be present before
|
||||
any public tag, package publish, plugin submission, or announcement action.
|
||||
|
||||
## Reference Pressure
|
||||
|
||||
@@ -71,6 +75,9 @@ later, but only after the local event model is useful enough to trust.
|
||||
relying on local work-item status for a tracked repository.
|
||||
7. Use ECC2 tool logs for risky operations, conflict analysis, and handoff
|
||||
review before increasing autonomy.
|
||||
8. Re-run the release-safety evidence checks before any public release action:
|
||||
publication readiness, supply-chain incident response, workflow-security
|
||||
validation, package surface, and release-surface tests.
|
||||
|
||||
The end-state is practical: before asking ECC to run larger multi-agent loops,
|
||||
the operator can prove the system has live status, durable session traces,
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# ECC v2.0.0-rc.1 Publication Evidence - 2026-05-13 Post-Hardening
|
||||
|
||||
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 base | `209abd403b7eaa968c6d4fa67be82e04b55706d6` |
|
||||
| Evidence branch | `docs/post-hardening-release-evidence-20260513` |
|
||||
| Evidence scope | Current `main` after PR #1850 and PR #1851 |
|
||||
| Git remote | `https://github.com/affaan-m/everything-claude-code.git` |
|
||||
| Local status caveat | Working tree had the unrelated untracked `docs/drafts/` directory |
|
||||
|
||||
The actual release operator should repeat these checks from the final release
|
||||
commit with a clean checkout before publishing.
|
||||
|
||||
## Queue And Release State
|
||||
|
||||
| Surface | Command | Result |
|
||||
| --- | --- | --- |
|
||||
| GitHub PRs and issues | `gh pr list` / `gh issue list` across trunk, AgentShield, and JARVIS | 0 open PRs and 0 open issues on accessible `affaan-m` repos |
|
||||
| Trunk discussions | GraphQL discussion count for `affaan-m/everything-claude-code` | 0 open discussions |
|
||||
| Dependabot alerts | Dependabot alert API for trunk, AgentShield, and JARVIS | 0 open alerts |
|
||||
| Release state | `gh release view v2.0.0-rc.1` | Still not created; release remains approval-gated |
|
||||
|
||||
ECC-Tools organization repo counts were not rechecked through the current
|
||||
GraphQL token in this pass because the token cannot resolve those org repos.
|
||||
The prior post-#42 local checkout handoff recorded both ECC-Tools repos at
|
||||
0 open PRs and 0 open issues.
|
||||
|
||||
## Hardening Landed Since Previous Evidence
|
||||
|
||||
| PR | Merge commit | Evidence |
|
||||
| --- | --- | --- |
|
||||
| #1850 | `248673271455e9dc85b8add2a6ab76107b718639` | Removed `Bash` tool access from read-only analyzer agents and zh-CN copies; AgentShield high findings on that surface dropped 21 -> 18 with no new high findings |
|
||||
| #1851 | `209abd403b7eaa968c6d4fa67be82e04b55706d6` | Disabled `actions/checkout` credential persistence in write-permission workflows and added a workflow-security validator rule to keep that guard in place |
|
||||
|
||||
## Required Command Evidence
|
||||
|
||||
| Evidence | Command | Result |
|
||||
| --- | --- | --- |
|
||||
| Harness audit | `npm run harness:audit -- --format json` | `overall_score: 70`, `max_score: 70`, no top actions |
|
||||
| Adapter scorecard | `npm run harness:adapters -- --check` | `Harness Adapter Compliance: PASS`; 11 adapters |
|
||||
| Observability readiness | `npm run observability:ready -- --format json` | `overall_score: 21`, `max_score: 21`, `ready: true`, no top actions; includes Release Safety 3/3 |
|
||||
| Workflow security validator | `node scripts/ci/validate-workflow-security.js` | Validated 7 workflow files |
|
||||
| Workflow validator tests | `node tests/ci/validate-workflow-security.test.js` | Passed 14/14 |
|
||||
| Release surface | `node tests/docs/ecc2-release-surface.test.js` | Passed 18/18 |
|
||||
| Package surface | `node tests/scripts/npm-publish-surface.test.js` | Passed 2/2 |
|
||||
| Root suite | `node tests/run-all.js` | Passed 2381/2381, 0 failed |
|
||||
| Markdown lint | `npx markdownlint-cli '**/*.md' --ignore node_modules --ignore docs/drafts` | Passed |
|
||||
| Rust surface | `cd ecc2 && cargo test` | Passed 462/462; warnings only for unused functions/fields |
|
||||
| GitGuardian Security Checks | GitHub check on post-hardening security PRs | Passed before merge |
|
||||
|
||||
## Supply-Chain Evidence
|
||||
|
||||
| Surface | Command or check | Result |
|
||||
| --- | --- | --- |
|
||||
| Local npm vulnerability audit | `npm audit --json` | 0 vulnerabilities |
|
||||
| Local npm signature audit | `npm audit signatures` | 241 verified registry signatures and 30 verified attestations |
|
||||
| Rust advisory audit | `cd ecc2 && cargo audit -q` | Passed silently |
|
||||
| TanStack / Mini Shai-Hulud IOC check | Grep for affected package namespaces, payload filenames, and known commit marker | No runtime or lockfile dependency on affected packages; no worm IOC matches |
|
||||
| GitGuardian Security Checks | GitHub check on post-hardening security PRs | Passed before merge |
|
||||
|
||||
## External Advisory Mapping
|
||||
|
||||
The May 2026 TanStack incident maps to ECC release risk through three workflow
|
||||
classes:
|
||||
|
||||
- `pull_request_target` workflows that execute or checkout untrusted PR code;
|
||||
- shared dependency caches crossing fork, base, and release workflow trust
|
||||
boundaries;
|
||||
- release jobs with writable tokens or OIDC tokens exposed to subsequent
|
||||
process execution.
|
||||
|
||||
ECC's current guardrails cover those classes through:
|
||||
|
||||
- rejection of untrusted checkout refs in `workflow_run` and
|
||||
`pull_request_target` workflows;
|
||||
- rejection of shared caches in `pull_request_target` and `id-token: write`
|
||||
workflows;
|
||||
- mandatory `npm audit signatures` when workflows run `npm audit`;
|
||||
- mandatory `npm ci --ignore-scripts` in workflows with write permissions;
|
||||
- mandatory `persist-credentials: false` on `actions/checkout` in workflows
|
||||
with write permissions.
|
||||
|
||||
## Blockers Still Requiring Approval Or External Action
|
||||
|
||||
- Create or verify GitHub prerelease `v2.0.0-rc.1`.
|
||||
- Publish `ecc-universal@2.0.0-rc.1` with npm dist-tag `next`.
|
||||
- Create and push the Claude plugin tag only after explicit approval.
|
||||
- Confirm the live Claude/Codex/OpenCode marketplace submission path or record
|
||||
the manual submission owner and status.
|
||||
- Verify ECC Tools billing/App/Marketplace claims before using them in launch
|
||||
copy.
|
||||
- Refresh announcement copy with live URLs after release and package/plugin
|
||||
URLs exist.
|
||||
@@ -10,6 +10,8 @@ For the May 12 dry-run evidence pass, see
|
||||
[`publication-evidence-2026-05-12.md`](publication-evidence-2026-05-12.md).
|
||||
For the May 13 release-readiness evidence refresh, see
|
||||
[`publication-evidence-2026-05-13.md`](publication-evidence-2026-05-13.md).
|
||||
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).
|
||||
|
||||
## Release Identity Matrix
|
||||
|
||||
@@ -39,6 +41,7 @@ For the May 13 release-readiness evidence refresh, see
|
||||
| OpenCode package | Build output is regenerated from source and package metadata is current | `npm run build:opencode` | `Blocker: none for local build; public distribution still follows npm/plugin release` | Package owner | Evidence recorded |
|
||||
| ECC Tools billing reference | Any billing claim links to verified Marketplace/App state | `gh api repos/ECC-Tools/ECC-Tools` plus app/marketplace URL check | `Blocker:` | ECC Tools owner | Pending |
|
||||
| Announcement copy | X, LinkedIn, GitHub release, and longform copy point to live URLs | `rg -n "TODO" docs/releases/2.0.0-rc.1` and repeat for `TBD` | `Blocker:` | Release owner | Pending |
|
||||
| Privileged workflow hardening | Release and maintenance workflows avoid persisted checkout tokens | `node scripts/ci/validate-workflow-security.js` | `Blocker:` | Release owner | Evidence recorded in post-hardening refresh |
|
||||
|
||||
## Required Command Evidence
|
||||
|
||||
@@ -49,8 +52,10 @@ Record the exact commit SHA and command output before any publication action:
|
||||
| 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/` |
|
||||
| Harness audit | `npm run harness:audit -- --format json` | 70/70 passing | `publication-evidence-2026-05-13.md`: 70/70 |
|
||||
| Adapter scorecard | `npm run harness:adapters -- --check` | PASS | `publication-evidence-2026-05-13.md`: PASS, 11 adapters |
|
||||
| Observability readiness | `npm run observability:ready` | 16/16 passing | `publication-evidence-2026-05-13.md`: 16/16, ready true |
|
||||
| Root suite | `node tests/run-all.js` | 0 failures | `publication-evidence-2026-05-13.md`: 2376 passed, 0 failed |
|
||||
| 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 |
|
||||
| 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-13-post-hardening.md`: npm, cargo, Dependabot, TanStack/Mini Shai-Hulud, and GitGuardian evidence |
|
||||
| Root suite | `node tests/run-all.js` | 0 failures | `publication-evidence-2026-05-13-post-hardening.md`: 2381 passed, 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 |
|
||||
| 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 |
|
||||
|
||||
@@ -8,7 +8,9 @@ they do not prove that the workflow executed the intended code path.
|
||||
## Current External Trigger
|
||||
|
||||
As of 2026-05-13, the active incident class is the May 2026 TanStack npm
|
||||
supply-chain compromise:
|
||||
supply-chain compromise. ECC also keeps Mini Shai-Hulud-style npm worm IOCs in
|
||||
the same release-safety sweep because both incident classes target package
|
||||
install/publish paths and developer credentials:
|
||||
|
||||
- TanStack reported 84 malicious versions across 42 `@tanstack/*` packages,
|
||||
published on 2026-05-11 between 19:20 and 19:26 UTC.
|
||||
|
||||
@@ -243,6 +243,24 @@
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lang:ruby",
|
||||
"family": "language",
|
||||
"description": "Ruby and Rails coding, testing, and security guidance. Resolves through framework-language and security modules.",
|
||||
"modules": [
|
||||
"framework-language",
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "framework:rails",
|
||||
"family": "framework",
|
||||
"description": "Rails 8 application guidance for MVC, Hotwire, Solid Queue/Cache/Cable, authentication, testing, and security.",
|
||||
"modules": [
|
||||
"framework-language",
|
||||
"security"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lang:rust",
|
||||
"family": "language",
|
||||
|
||||
@@ -21,6 +21,7 @@ rules/
|
||||
├── web/ # Web and frontend specific
|
||||
├── swift/ # Swift specific
|
||||
├── php/ # PHP specific
|
||||
├── ruby/ # Ruby / Rails specific
|
||||
└── arkts/ # HarmonyOS / ArkTS specific
|
||||
```
|
||||
|
||||
@@ -40,6 +41,7 @@ rules/
|
||||
./install.sh web
|
||||
./install.sh swift
|
||||
./install.sh php
|
||||
./install.sh ruby
|
||||
./install.sh arkts
|
||||
|
||||
# Install multiple languages at once
|
||||
@@ -66,6 +68,7 @@ cp -r rules/golang ~/.claude/rules/golang
|
||||
cp -r rules/web ~/.claude/rules/web
|
||||
cp -r rules/swift ~/.claude/rules/swift
|
||||
cp -r rules/php ~/.claude/rules/php
|
||||
cp -r rules/ruby ~/.claude/rules/ruby
|
||||
cp -r rules/arkts ~/.claude/rules/arkts
|
||||
|
||||
# Attention ! ! ! Configure according to your actual project requirements; the configuration here is for reference only.
|
||||
|
||||
46
rules/ruby/coding-style.md
Normal file
46
rules/ruby/coding-style.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rb"
|
||||
- "**/*.rake"
|
||||
- "**/Gemfile"
|
||||
- "**/*.gemspec"
|
||||
- "**/config.ru"
|
||||
---
|
||||
# Ruby Coding Style
|
||||
|
||||
> This file extends [common/coding-style.md](../common/coding-style.md) with Ruby and Rails specific content.
|
||||
|
||||
## Standards
|
||||
|
||||
- Target **Ruby 3.3+** for new Rails work unless the project already pins an older supported runtime.
|
||||
- Enable **YJIT** in production only after measuring boot time, memory, and request/job throughput.
|
||||
- Add `# frozen_string_literal: true` to new Ruby files when the project uses that convention.
|
||||
- Prefer clear Ruby over clever metaprogramming; isolate DSL-heavy code behind narrow, tested boundaries.
|
||||
|
||||
## Formatting And Linting
|
||||
|
||||
- Use the project's checked-in RuboCop config. For Rails 8+ apps, start from `rubocop-rails-omakase` and customize only where the codebase has a real convention.
|
||||
- Keep formatter/linter commands behind binstubs or scripts so CI and local runs match:
|
||||
|
||||
```bash
|
||||
bundle exec rubocop
|
||||
bundle exec rubocop -A
|
||||
```
|
||||
|
||||
- Do not silence cops inline unless the exception is narrow, documented, and harder to express cleanly in code.
|
||||
|
||||
## Rails Style
|
||||
|
||||
- Follow Rails naming and directory conventions before adding custom structure.
|
||||
- Keep controllers transport-focused: authentication, authorization, parameter handling, response shape.
|
||||
- Put reusable domain behavior in models, concerns, service objects, query objects, or form objects based on actual complexity, not as default ceremony.
|
||||
- Prefer `bin/rails`, `bin/rake`, and checked-in binstubs over globally installed commands.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- Rescue specific exceptions. Avoid broad `rescue StandardError` blocks unless they re-raise or preserve enough context for operators.
|
||||
- Use `ActiveSupport::Notifications` or the app's logger for operational events; do not leave `puts`, `pp`, or `debugger` in committed application code.
|
||||
|
||||
## Reference
|
||||
|
||||
See skill: `backend-patterns` for broader service/repository layering guidance.
|
||||
37
rules/ruby/hooks.md
Normal file
37
rules/ruby/hooks.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rb"
|
||||
- "**/*.rake"
|
||||
- "**/Gemfile"
|
||||
- "**/Gemfile.lock"
|
||||
- "**/config/routes.rb"
|
||||
---
|
||||
# Ruby Hooks
|
||||
|
||||
> This file extends [common/hooks.md](../common/hooks.md) with Ruby and Rails specific content.
|
||||
|
||||
## PostToolUse Hooks
|
||||
|
||||
Configure project-local hooks to prefer binstubs and checked-in tooling:
|
||||
|
||||
- **RuboCop**: run `bundle exec rubocop -A <file>` or the project's safer formatter command after Ruby edits.
|
||||
- **Brakeman**: run `bundle exec brakeman --no-pager` after security-sensitive Rails changes.
|
||||
- **Tests**: run the narrowest matching `bin/rails test ...` or `bundle exec rspec ...` command for touched files.
|
||||
- **Bundler audit**: run `bundle exec bundle-audit check --update` when `Gemfile` or `Gemfile.lock` changes and the project has bundler-audit installed.
|
||||
|
||||
## Warnings
|
||||
|
||||
- Warn on committed `debugger`, `binding.irb`, `binding.pry`, `puts`, `pp`, or `p` calls in application code.
|
||||
- Warn when an edit disables CSRF protection, expands mass-assignment, or adds raw SQL without parameterization.
|
||||
- Warn when a migration changes data destructively without a reversible path or documented rollout plan.
|
||||
|
||||
## CI Gate Suggestions
|
||||
|
||||
```bash
|
||||
bundle exec rubocop
|
||||
bundle exec brakeman --no-pager
|
||||
bin/rails test
|
||||
bundle exec rspec
|
||||
```
|
||||
|
||||
Use only the commands that are present in the project; do not install new hook dependencies without maintainer approval.
|
||||
44
rules/ruby/patterns.md
Normal file
44
rules/ruby/patterns.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rb"
|
||||
- "**/*.rake"
|
||||
- "**/Gemfile"
|
||||
- "**/app/**/*.erb"
|
||||
- "**/config/routes.rb"
|
||||
---
|
||||
# Ruby Patterns
|
||||
|
||||
> This file extends [common/patterns.md](../common/patterns.md) with Ruby and Rails specific content.
|
||||
|
||||
## Rails Way First
|
||||
|
||||
- Start with plain Rails MVC and Active Record conventions for small and medium features.
|
||||
- Introduce service objects, query objects, form objects, decorators, or presenters when the model/controller boundary is carrying multiple responsibilities.
|
||||
- Name extracted objects after the business operation they perform, not after generic layers like `Manager` or `Processor`.
|
||||
|
||||
## Persistence
|
||||
|
||||
- Prefer PostgreSQL for multi-host production Rails apps unless the existing platform has a clear reason for MySQL or SQLite.
|
||||
- Treat Rails 8 SQLite-backed defaults as viable for single-host or modest deployments, not as an automatic fit for shared multi-service systems.
|
||||
- Keep raw SQL behind query objects or model scopes and parameterize every dynamic value.
|
||||
|
||||
## Background Jobs And Runtime Services
|
||||
|
||||
- Use **Solid Queue** for greenfield Rails 8 apps with modest throughput and simple deployment needs.
|
||||
- Use **Sidekiq** when the app needs mature observability, high throughput, existing Redis infrastructure, or Pro/Enterprise features.
|
||||
- Use **Solid Cache** and **Solid Cable** when their deployment model matches the app; use Redis when shared cross-service behavior, high fanout, or advanced data structures matter.
|
||||
|
||||
## Frontend
|
||||
|
||||
- Prefer **Hotwire** with Turbo, Stimulus, Importmap, and Propshaft for server-rendered Rails apps.
|
||||
- Use React, Vue, Inertia.js, or a separate SPA when interaction complexity, existing product architecture, or team ownership justifies the extra client surface.
|
||||
- Keep view components, partials, and presenters focused on rendering decisions; keep persistence and authorization out of templates.
|
||||
|
||||
## Authentication
|
||||
|
||||
- Use the Rails 8 authentication generator for straightforward session auth and password reset needs.
|
||||
- Use Devise or another established auth system when requirements include OAuth, MFA, confirmable/lockable flows, multi-model auth, or a large existing Devise footprint.
|
||||
|
||||
## Reference
|
||||
|
||||
See skill: `backend-patterns` for service boundaries and adapter patterns.
|
||||
51
rules/ruby/security.md
Normal file
51
rules/ruby/security.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rb"
|
||||
- "**/*.rake"
|
||||
- "**/Gemfile"
|
||||
- "**/Gemfile.lock"
|
||||
- "**/config/routes.rb"
|
||||
- "**/config/credentials*.yml.enc"
|
||||
---
|
||||
# Ruby Security
|
||||
|
||||
> This file extends [common/security.md](../common/security.md) with Ruby and Rails specific content.
|
||||
|
||||
## Rails Defaults
|
||||
|
||||
- Keep CSRF protection enabled for state-changing browser requests.
|
||||
- Use strong parameters or typed boundary objects before mass assignment.
|
||||
- Store secrets in Rails credentials, environment variables, or a secret manager. Never commit plaintext keys, tokens, private credentials, or copied `.env` values.
|
||||
|
||||
## SQL And Active Record
|
||||
|
||||
- Prefer Active Record query APIs and parameterized SQL.
|
||||
- Never interpolate request, cookie, header, job, or webhook values into SQL strings.
|
||||
- Scope model callbacks carefully; security-sensitive side effects should be explicit and covered by tests.
|
||||
|
||||
## Authentication And Sessions
|
||||
|
||||
- Use the Rails 8 authentication generator for simple session auth, or Devise when OAuth, MFA, confirmable, lockable, multi-model auth, or existing Devise conventions are required.
|
||||
- Rotate sessions after sign-in and privilege changes.
|
||||
- Protect account recovery flows with expiry, single-use tokens, rate limiting, and audit logging.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Run dependency checks when the lockfile changes:
|
||||
|
||||
```bash
|
||||
bundle audit check --update
|
||||
bundle exec brakeman --no-pager
|
||||
```
|
||||
|
||||
- Review new gems for maintainer activity, native extension risk, transitive dependencies, and whether the same behavior can be implemented with Rails core.
|
||||
|
||||
## Web Safety
|
||||
|
||||
- Escape template output by default. Treat `html_safe`, `raw`, and custom sanitizers as security-sensitive code.
|
||||
- Validate file uploads by content type, extension, size, and storage destination.
|
||||
- Treat background jobs, webhooks, Action Cable messages, and Turbo Stream inputs as untrusted boundaries.
|
||||
|
||||
## Reference
|
||||
|
||||
See skill: `security-review` for secure-by-default review patterns.
|
||||
51
rules/ruby/testing.md
Normal file
51
rules/ruby/testing.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.rb"
|
||||
- "**/*.rake"
|
||||
- "**/Gemfile"
|
||||
- "**/test/**/*.rb"
|
||||
- "**/spec/**/*.rb"
|
||||
- "**/config/routes.rb"
|
||||
---
|
||||
# Ruby Testing
|
||||
|
||||
> This file extends [common/testing.md](../common/testing.md) with Ruby and Rails specific content.
|
||||
|
||||
## Framework
|
||||
|
||||
- Use **Minitest** when the Rails app follows the default Rails test stack.
|
||||
- Use **RSpec** when it is already established in the project or the team has explicit production conventions around it.
|
||||
- Do not mix Minitest and RSpec inside the same feature area without a migration reason.
|
||||
|
||||
## Test Pyramid
|
||||
|
||||
- Put fast domain behavior in model, service, query, policy, and job tests.
|
||||
- Use request/controller tests for HTTP contracts, auth behavior, redirects, status codes, and response shapes.
|
||||
- Use system tests with Capybara for browser-critical flows only; keep them focused and stable.
|
||||
- Cover background jobs with unit tests for behavior and integration tests for queue/enqueue contracts.
|
||||
|
||||
## Fixtures And Factories
|
||||
|
||||
- Use Rails fixtures when they are the project default and the data graph is small.
|
||||
- Use `factory_bot` when scenarios need explicit object construction or complex traits.
|
||||
- Keep test data close to the behavior being asserted; avoid global fixtures that hide setup cost.
|
||||
|
||||
## Commands
|
||||
|
||||
Prefer project-local commands:
|
||||
|
||||
```bash
|
||||
bin/rails test
|
||||
bin/rails test test/models/user_test.rb
|
||||
bundle exec rspec
|
||||
bundle exec rspec spec/models/user_spec.rb
|
||||
```
|
||||
|
||||
## Coverage
|
||||
|
||||
- Use SimpleCov when coverage is enforced; keep thresholds in CI and avoid gaming branch coverage with low-value tests.
|
||||
- Add regression tests for bug fixes before changing production code.
|
||||
|
||||
## Reference
|
||||
|
||||
See skill: `tdd-workflow` for the repo-wide RED -> GREEN -> REFACTOR loop.
|
||||
@@ -42,7 +42,374 @@ const EDIT_WRITE_HOOK_ID = 'pre:edit-write:gateguard-fact-force';
|
||||
const BASH_HOOK_ID = 'pre:bash:gateguard-fact-force';
|
||||
const ECC_DISABLE_VALUES = new Set(['0', 'false', 'off', 'disabled', 'disable']);
|
||||
|
||||
const DESTRUCTIVE_BASH = /\b(rm\s+-rf|git\s+reset\s+--hard|git\s+checkout\s+--|git\s+clean\s+-f|drop\s+table|delete\s+from|truncate|git\s+push\s+--force(?!-with-lease)|git\s+commit\s+--amend|dd\s+if=)\b/i;
|
||||
// SQL-keyword + dd patterns stay as a single regex — they are stable
|
||||
// phrases without shell-flag ordering concerns. Quoted strings are
|
||||
// stripped before this regex runs so a commit message mentioning
|
||||
// "drop table" no longer triggers a false positive.
|
||||
const DESTRUCTIVE_SQL_DD = /\b(drop\s+table|delete\s+from|truncate|dd\s+if=)\b/i;
|
||||
|
||||
/**
|
||||
* Strip the contents of single- and double-quoted strings so phrases
|
||||
* mentioned inside a commit message or echoed argument do not trigger
|
||||
* the destructive detector. Command substitutions are scanned separately
|
||||
* before this runs because they execute even inside double quotes.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
*/
|
||||
function stripQuotedStrings(input) {
|
||||
return input
|
||||
.replace(/'(?:[^'\\]|\\.)*'/g, "''")
|
||||
.replace(/"(?:[^"\\]|\\.)*"/g, '""');
|
||||
}
|
||||
|
||||
/**
|
||||
* Promote subshell delimiters to top-level segment separators so the
|
||||
* destructive check applies inside `$(...)` and backtick subshells.
|
||||
* Without this, `echo y | $(rm -rf /tmp)` and ``echo y | `rm -rf /tmp` ``
|
||||
* slip past the segment splitter because the destructive command lives
|
||||
* inside a sub-expression. Run iteratively to handle a layer of nesting.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
*/
|
||||
function explodeSubshells(input) {
|
||||
let out = input;
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
const before = out;
|
||||
out = out.replace(/\$\(([^()`]*)\)/g, ';$1;');
|
||||
out = out.replace(/`([^`]*)`/g, ';$1;');
|
||||
if (out === before) break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract executable command-substitution bodies from a shell line. Single
|
||||
* quotes are literal, so substitutions inside them are ignored; double quotes
|
||||
* still permit substitutions, so those bodies are scanned before quoted text
|
||||
* is stripped.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function extractCommandSubstitutions(input) {
|
||||
const source = String(input || '');
|
||||
const substitutions = [];
|
||||
let inSingle = false;
|
||||
let inDouble = false;
|
||||
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
const ch = source[i];
|
||||
const prev = source[i - 1];
|
||||
|
||||
if (ch === '\\' && !inSingle) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === "'" && !inDouble && prev !== '\\') {
|
||||
inSingle = !inSingle;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '"' && !inSingle && prev !== '\\') {
|
||||
inDouble = !inDouble;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inSingle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '`') {
|
||||
let body = '';
|
||||
i += 1;
|
||||
while (i < source.length) {
|
||||
const inner = source[i];
|
||||
if (inner === '\\') {
|
||||
body += inner;
|
||||
if (i + 1 < source.length) {
|
||||
body += source[i + 1];
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (inner === '`') {
|
||||
break;
|
||||
}
|
||||
body += inner;
|
||||
i += 1;
|
||||
}
|
||||
if (body.trim()) {
|
||||
substitutions.push(body);
|
||||
substitutions.push(...extractCommandSubstitutions(body));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '$' && source[i + 1] === '(') {
|
||||
let depth = 1;
|
||||
let body = '';
|
||||
i += 2;
|
||||
while (i < source.length && depth > 0) {
|
||||
const inner = source[i];
|
||||
if (inner === '\\') {
|
||||
body += inner;
|
||||
if (i + 1 < source.length) {
|
||||
body += source[i + 1];
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (inner === '(') {
|
||||
depth += 1;
|
||||
} else if (inner === ')') {
|
||||
depth -= 1;
|
||||
if (depth === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
body += inner;
|
||||
i += 1;
|
||||
}
|
||||
if (body.trim()) {
|
||||
substitutions.push(body);
|
||||
substitutions.push(...extractCommandSubstitutions(body));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return substitutions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a command line into top-level segments at unquoted shell
|
||||
* separators (`;`, `|`, `&`, `&&`, `||`) and across subshells
|
||||
* (`$(...)` / backticks). Quoted strings are stripped first so
|
||||
* separators inside quotes are not split on. Per-segment comments
|
||||
* are also stripped.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function splitCommandSegments(input) {
|
||||
const stripped = explodeSubshells(stripQuotedStrings(input));
|
||||
return stripped
|
||||
.split(/[;|&]+/)
|
||||
.map(segment => segment.replace(/(^|\s)#.*/, '$1').trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenize a single command segment by whitespace. Quoted strings
|
||||
* are already collapsed to empty quotes by `stripQuotedStrings`, so
|
||||
* naive whitespace splitting is sufficient.
|
||||
*
|
||||
* @param {string} segment
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function tokenize(segment) {
|
||||
return segment.split(/\s+/).filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip a leading path and trailing `.exe` from a command token so
|
||||
* `/usr/bin/git`, `git.exe`, and `GIT` all normalize to `git`.
|
||||
*
|
||||
* @param {string} token
|
||||
* @returns {string}
|
||||
*/
|
||||
function commandBasename(token) {
|
||||
if (!token) return '';
|
||||
return token.replace(/^.*[\\/]/, '').replace(/\.exe$/i, '').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect `rm` invocations that recursively force-delete files. Handles
|
||||
* combined (`-rf`, `-fr`, `-Rf`) and split (`-r -f`) flag forms.
|
||||
*
|
||||
* @param {string[]} tokens
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isDestructiveRm(tokens) {
|
||||
if (tokens.length === 0 || commandBasename(tokens[0]) !== 'rm') return false;
|
||||
let hasR = false;
|
||||
let hasF = false;
|
||||
for (const t of tokens.slice(1)) {
|
||||
if (t === '--recursive') {
|
||||
hasR = true;
|
||||
continue;
|
||||
}
|
||||
if (t === '--force') {
|
||||
hasF = true;
|
||||
continue;
|
||||
}
|
||||
if (!t.startsWith('-') || t.startsWith('--')) continue;
|
||||
const body = t.slice(1);
|
||||
if (/[rR]/.test(body)) hasR = true;
|
||||
if (/f/.test(body)) hasF = true;
|
||||
}
|
||||
return hasR && hasF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the git subcommand within a token list, skipping over git's
|
||||
* global options like `-c key=value`, `-C <path>`, `--git-dir=...`,
|
||||
* `--work-tree=...`, `--namespace=...`, `--super-prefix=...`.
|
||||
*
|
||||
* @param {string[]} tokens
|
||||
* @returns {{ command: string, rest: string[] } | null}
|
||||
*/
|
||||
function findGitSubcommand(tokens) {
|
||||
if (tokens.length === 0 || commandBasename(tokens[0]) !== 'git') return null;
|
||||
const valueConsumingShort = new Set(['-c', '-C']);
|
||||
const valueConsumingLong = new Set(['--git-dir', '--work-tree', '--namespace', '--super-prefix']);
|
||||
let i = 1;
|
||||
while (i < tokens.length) {
|
||||
const t = tokens[i];
|
||||
if (valueConsumingShort.has(t) || valueConsumingLong.has(t)) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
if (t.startsWith('--git-dir=') || t.startsWith('--work-tree=') || t.startsWith('--namespace=') || t.startsWith('--super-prefix=')) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (t.startsWith('-')) {
|
||||
// Unknown global option — skip without consuming a value.
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
return { command: t.toLowerCase(), rest: tokens.slice(i + 1) };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect destructive `git` invocations: `reset --hard`, `checkout --`,
|
||||
* `clean -f...`, `push --force` (but not `--force-with-lease`),
|
||||
* `commit --amend`, `rm -rf`.
|
||||
*
|
||||
* @param {string[]} tokens
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isDestructiveGit(tokens) {
|
||||
const sub = findGitSubcommand(tokens);
|
||||
if (!sub) return false;
|
||||
const { command, rest } = sub;
|
||||
|
||||
if (command === 'reset') {
|
||||
return rest.includes('--hard');
|
||||
}
|
||||
|
||||
if (command === 'checkout') {
|
||||
return rest.includes('--');
|
||||
}
|
||||
|
||||
if (command === 'clean') {
|
||||
// `git clean -f`, `-fd`, `-fdx`, `-df`, `--force`
|
||||
return rest.some(t => {
|
||||
if (t === '--force') return true;
|
||||
if (!t.startsWith('-') || t.startsWith('--')) return false;
|
||||
return t.slice(1).includes('f');
|
||||
});
|
||||
}
|
||||
|
||||
if (command === 'push') {
|
||||
// Only `--force-with-lease` qualifies as a safety-checked force.
|
||||
// `--force-if-includes` is a no-op when used WITHOUT
|
||||
// `--force-with-lease` (per git-scm.com/docs/git-push), and when
|
||||
// combined with a bare `--force` the bare force is still in effect.
|
||||
// So `--force --force-if-includes` must be treated as destructive.
|
||||
//
|
||||
// A `+` refspec prefix (e.g. `git push origin +main`,
|
||||
// `+refs/heads/main:refs/heads/main`) also forces a non-fast-forward
|
||||
// update of that ref and is destructive on its own.
|
||||
let withLease = false;
|
||||
let bareForce = false;
|
||||
let plusRefspecForce = false;
|
||||
for (const t of rest) {
|
||||
if (t === '--force-with-lease' || t.startsWith('--force-with-lease=')) {
|
||||
withLease = true;
|
||||
continue;
|
||||
}
|
||||
if (t === '--force' || t.startsWith('--force=')) {
|
||||
bareForce = true;
|
||||
continue;
|
||||
}
|
||||
if (t.startsWith('-') && !t.startsWith('--') && t.slice(1).includes('f')) {
|
||||
bareForce = true;
|
||||
continue;
|
||||
}
|
||||
// Refspec prefix: `+<src>[:<dst>]`. Match tokens like `+main`,
|
||||
// `+refs/heads/main`, `+HEAD:branch`, `+:branch`. Exclude bare
|
||||
// `+` and numeric-only `+123` which are not refspecs.
|
||||
if (t.startsWith('+') && t.length > 1 && /^\+(?:[a-zA-Z_/.:]|HEAD)/.test(t)) {
|
||||
plusRefspecForce = true;
|
||||
}
|
||||
}
|
||||
return bareForce || (plusRefspecForce && !withLease);
|
||||
}
|
||||
|
||||
if (command === 'commit') {
|
||||
return rest.includes('--amend');
|
||||
}
|
||||
|
||||
if (command === 'rm') {
|
||||
// `git rm -r` / `-rf` / `-r -f` — destructive within the index too.
|
||||
let hasR = false;
|
||||
for (const t of rest) {
|
||||
if (!t.startsWith('-') || t.startsWith('--')) continue;
|
||||
if (/[rR]/.test(t.slice(1))) hasR = true;
|
||||
}
|
||||
return hasR;
|
||||
}
|
||||
|
||||
if (command === 'switch') {
|
||||
// `git switch` can discard local working-tree changes in three forms:
|
||||
// --discard-changes explicit discard
|
||||
// --force / -f ignore conflicts and overwrite
|
||||
// -C <branch> force-create (overwrites existing branch)
|
||||
return rest.some(t => {
|
||||
if (t === '--discard-changes' || t === '--force') return true;
|
||||
if (!t.startsWith('-') || t.startsWith('--')) return false;
|
||||
// Short combined form: -f, -fC, -Cf, -C
|
||||
const body = t.slice(1);
|
||||
return /[fC]/.test(body);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether a bash command line contains a destructive action
|
||||
* the fact-forcing gate should challenge. Combines SQL-keyword
|
||||
* detection (regex on quote-stripped input) with per-segment shell
|
||||
* tokenization for shell commands.
|
||||
*
|
||||
* @param {string} command
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isDestructiveBash(command) {
|
||||
// The SQL/dd phrases live in command bodies, not as flag-bearing
|
||||
// arguments, so we still match them by regex — but on the input
|
||||
// after quoting AND subshell delimiters are normalized so phrases
|
||||
// inside `$(...)` or backticks are also caught.
|
||||
const raw = String(command || '');
|
||||
const flattened = explodeSubshells(stripQuotedStrings(raw));
|
||||
if (DESTRUCTIVE_SQL_DD.test(flattened)) return true;
|
||||
|
||||
const segments = [raw, ...extractCommandSubstitutions(raw)].flatMap(splitCommandSegments);
|
||||
for (const segment of segments) {
|
||||
if (DESTRUCTIVE_SQL_DD.test(stripQuotedStrings(segment))) return true;
|
||||
const tokens = tokenize(segment);
|
||||
if (isDestructiveRm(tokens)) return true;
|
||||
if (isDestructiveGit(tokens)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- State management (per-session, atomic writes, bounded) ---
|
||||
|
||||
@@ -483,7 +850,7 @@ function run(rawInput) {
|
||||
return rawInput;
|
||||
}
|
||||
|
||||
if (DESTRUCTIVE_BASH.test(command)) {
|
||||
if (isDestructiveBash(command)) {
|
||||
// Gate destructive commands on first attempt; allow retry after facts presented
|
||||
const key = '__destructive__' + crypto.createHash('sha256').update(command).digest('hex').slice(0, 16);
|
||||
if (!isChecked(key)) {
|
||||
|
||||
@@ -19,7 +19,8 @@ const {
|
||||
getTempDir,
|
||||
writeFile,
|
||||
readStdinJson,
|
||||
log
|
||||
log,
|
||||
output
|
||||
} = require('../lib/utils');
|
||||
|
||||
async function resolveSessionId() {
|
||||
@@ -77,14 +78,25 @@ async function main() {
|
||||
writeFile(counterFile, String(count));
|
||||
}
|
||||
|
||||
// Suggest compact after threshold tool calls
|
||||
// Suggest compact after threshold tool calls.
|
||||
//
|
||||
// log() writes to stderr (debug log). Per the Claude Code hooks guide,
|
||||
// non-blocking PreToolUse stderr (exit 0) is only written to the debug log;
|
||||
// it does not reach the model. To inject a user-facing suggestion without
|
||||
// blocking the tool call, emit structured JSON to stdout with
|
||||
// hookSpecificOutput.additionalContext — the documented mechanism for
|
||||
// PreToolUse hooks to add context to the next model turn.
|
||||
if (count === threshold) {
|
||||
log(`[StrategicCompact] ${threshold} tool calls reached - consider /compact if transitioning phases`);
|
||||
const msg = `[StrategicCompact] ${threshold} tool calls reached - consider /compact if transitioning phases`;
|
||||
log(msg);
|
||||
output({ hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });
|
||||
}
|
||||
|
||||
// Suggest at regular intervals after threshold (every 25 calls from threshold)
|
||||
if (count > threshold && (count - threshold) % 25 === 0) {
|
||||
log(`[StrategicCompact] ${count} tool calls - good checkpoint for /compact if context is stale`);
|
||||
const msg = `[StrategicCompact] ${count} tool calls - good checkpoint for /compact if context is stale`;
|
||||
log(msg);
|
||||
output({ hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
|
||||
@@ -51,6 +51,8 @@ const LEGACY_LANGUAGE_ALIAS_TO_CANONICAL = Object.freeze({
|
||||
perl: 'perl',
|
||||
php: 'php',
|
||||
python: 'python',
|
||||
rails: 'ruby',
|
||||
ruby: 'ruby',
|
||||
rust: 'rust',
|
||||
swift: 'swift',
|
||||
typescript: 'typescript',
|
||||
@@ -66,6 +68,7 @@ const LEGACY_LANGUAGE_EXTRA_MODULE_IDS = Object.freeze({
|
||||
perl: [],
|
||||
php: [],
|
||||
python: ['framework-language'],
|
||||
ruby: ['framework-language', 'security'],
|
||||
rust: ['framework-language'],
|
||||
swift: [],
|
||||
typescript: ['framework-language'],
|
||||
|
||||
@@ -127,6 +127,13 @@ function buildChecks(rootDir) {
|
||||
const progressSyncContract = readText(rootDir, 'docs/architecture/progress-sync-contract.md');
|
||||
const gaRoadmap = readText(rootDir, 'docs/ECC-2.0-GA-ROADMAP.md');
|
||||
const workItems = readText(rootDir, 'scripts/work-items.js');
|
||||
const publicationReadiness = readText(rootDir, 'docs/releases/2.0.0-rc.1/publication-readiness.md');
|
||||
const postHardeningEvidence = readText(rootDir, 'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-13-post-hardening.md');
|
||||
const supplyChainIncidentResponse = readText(rootDir, 'docs/security/supply-chain-incident-response.md');
|
||||
const workflowSecurityValidator = readText(rootDir, 'scripts/ci/validate-workflow-security.js');
|
||||
const workflowSecurityValidatorTests = readText(rootDir, 'tests/ci/validate-workflow-security.test.js');
|
||||
const publishSurfaceTest = readText(rootDir, 'tests/scripts/npm-publish-surface.test.js');
|
||||
const releaseSurfaceTest = readText(rootDir, 'tests/docs/ecc2-release-surface.test.js');
|
||||
const hudStatusFixture = safeParseJson(readText(rootDir, 'examples/hud-status-contract.json')) || {};
|
||||
const quickstart = readText(rootDir, 'docs/releases/2.0.0-rc.1/quickstart.md');
|
||||
const releaseNotes = readText(rootDir, 'docs/releases/2.0.0-rc.1/release-notes.md');
|
||||
@@ -275,6 +282,56 @@ function buildChecks(rootDir) {
|
||||
]),
|
||||
fix: 'Add the progress sync contract, link it from the GA roadmap, and preserve work-items GitHub sync.'
|
||||
},
|
||||
{
|
||||
id: 'release-safety-evidence',
|
||||
category: 'Release Safety',
|
||||
points: 3,
|
||||
path: 'docs/releases/2.0.0-rc.1/publication-readiness.md',
|
||||
description: 'Release readiness includes package, workflow, and supply-chain evidence before publication',
|
||||
pass: fileExists(rootDir, 'docs/releases/2.0.0-rc.1/publication-readiness.md')
|
||||
&& fileExists(rootDir, 'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-13-post-hardening.md')
|
||||
&& fileExists(rootDir, 'docs/security/supply-chain-incident-response.md')
|
||||
&& fileExists(rootDir, 'scripts/ci/validate-workflow-security.js')
|
||||
&& fileExists(rootDir, 'tests/ci/validate-workflow-security.test.js')
|
||||
&& fileExists(rootDir, 'tests/scripts/npm-publish-surface.test.js')
|
||||
&& fileExists(rootDir, 'tests/docs/ecc2-release-surface.test.js')
|
||||
&& includesAll(publicationReadiness, [
|
||||
'Publication Gates',
|
||||
'Required Command Evidence',
|
||||
'Do Not Publish If',
|
||||
'npm dist-tag',
|
||||
'GitGuardian',
|
||||
'Dependabot alerts',
|
||||
'npm audit signatures'
|
||||
])
|
||||
&& includesAll(postHardeningEvidence, [
|
||||
'npm audit --json',
|
||||
'npm audit signatures',
|
||||
'cargo audit',
|
||||
'Dependabot alert API',
|
||||
'TanStack',
|
||||
'Mini Shai-Hulud',
|
||||
'GitGuardian Security Checks'
|
||||
])
|
||||
&& includesAll(supplyChainIncidentResponse, [
|
||||
'TanStack',
|
||||
'Mini Shai-Hulud',
|
||||
'npm audit signatures',
|
||||
'trusted publishing',
|
||||
'pull_request_target',
|
||||
'id-token: write'
|
||||
])
|
||||
&& includesAll(workflowSecurityValidator, [
|
||||
'persist-credentials: false',
|
||||
'npm audit signatures',
|
||||
'pull_request_target',
|
||||
'id-token: write'
|
||||
])
|
||||
&& includesAll(workflowSecurityValidatorTests, ['npm audit signatures', 'persist-credentials: false'])
|
||||
&& includesAll(publishSurfaceTest, ['npm pack', 'Python bytecode'])
|
||||
&& includesAll(releaseSurfaceTest, ['publication-readiness.md']),
|
||||
fix: 'Refresh publication readiness, post-hardening evidence, supply-chain response docs, workflow-security validator coverage, and package/release surface tests.'
|
||||
},
|
||||
{
|
||||
id: 'package-exposes-readiness-gate',
|
||||
category: 'Packaging',
|
||||
|
||||
@@ -82,6 +82,27 @@ def _normalize_remote_url(remote_url: str) -> str:
|
||||
return normalized.lower() if is_network else normalized
|
||||
|
||||
|
||||
def _stream_can_encode(text: str, stream=None) -> bool:
|
||||
stream = stream or sys.stdout
|
||||
encoding = getattr(stream, "encoding", None) or sys.getdefaultencoding()
|
||||
try:
|
||||
text.encode(encoding)
|
||||
except (LookupError, UnicodeEncodeError):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _confidence_bar(confidence, stream=None) -> str:
|
||||
try:
|
||||
filled = int(float(confidence) * 10)
|
||||
except (TypeError, ValueError):
|
||||
filled = 5
|
||||
filled = max(0, min(10, filled))
|
||||
|
||||
full, empty = ("\u2588", "\u2591") if _stream_can_encode("\u2588\u2591", stream) else ("#", ".")
|
||||
return full * filled + empty * (10 - filled)
|
||||
|
||||
|
||||
def _project_hash(value: str) -> str:
|
||||
return hashlib.sha256(value.encode("utf-8")).hexdigest()[:12]
|
||||
|
||||
@@ -550,7 +571,7 @@ def _print_instincts_by_domain(instincts: list[dict]) -> None:
|
||||
|
||||
for inst in sorted(domain_instincts, key=lambda x: -x.get('confidence', 0.5)):
|
||||
conf = inst.get('confidence', 0.5)
|
||||
conf_bar = '\u2588' * int(conf * 10) + '\u2591' * (10 - int(conf * 10))
|
||||
conf_bar = _confidence_bar(conf)
|
||||
trigger = inst.get('trigger', 'unknown trigger')
|
||||
scope_tag = f"[{inst.get('scope', '?')}]"
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ _find_cross_project_instincts = _mod._find_cross_project_instincts
|
||||
load_registry = _mod.load_registry
|
||||
_validate_instinct_id = _mod._validate_instinct_id
|
||||
_update_registry = _mod._update_registry
|
||||
_confidence_bar = _mod._confidence_bar
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
@@ -642,6 +643,39 @@ def test_cmd_status_with_instincts(patch_globals, monkeypatch, capsys):
|
||||
assert "GLOBAL" in out
|
||||
|
||||
|
||||
def test_confidence_bar_uses_unicode_when_supported():
|
||||
"""Confidence bars should retain block glyphs on UTF-8 streams."""
|
||||
stream = SimpleNamespace(encoding="utf-8")
|
||||
assert _confidence_bar(0.8, stream=stream) == "\u2588" * 8 + "\u2591" * 2
|
||||
|
||||
|
||||
def test_confidence_bar_uses_ascii_when_stream_rejects_block_glyphs():
|
||||
"""Windows cp1252 streams cannot encode block glyphs."""
|
||||
stream = SimpleNamespace(encoding="cp1252")
|
||||
assert _confidence_bar(0.8, stream=stream) == "########.."
|
||||
|
||||
|
||||
def test_print_instincts_by_domain_is_cp1252_safe(monkeypatch):
|
||||
"""Status rendering should not crash on Windows cp1252 stdout."""
|
||||
raw = io.BytesIO()
|
||||
stream = io.TextIOWrapper(raw, encoding="cp1252")
|
||||
monkeypatch.setattr(_mod.sys, "stdout", stream)
|
||||
|
||||
_mod._print_instincts_by_domain([{
|
||||
"id": "windows-safe",
|
||||
"trigger": "when stdout uses cp1252",
|
||||
"confidence": 0.8,
|
||||
"domain": "platform",
|
||||
"scope": "project",
|
||||
}])
|
||||
|
||||
stream.flush()
|
||||
out = raw.getvalue().decode("cp1252")
|
||||
assert "########.." in out
|
||||
assert "\u2588" not in out
|
||||
assert "\u2591" not in out
|
||||
|
||||
|
||||
def test_cmd_status_returns_int(patch_globals, monkeypatch):
|
||||
"""cmd_status should always return an int."""
|
||||
tree = patch_globals
|
||||
|
||||
@@ -130,12 +130,12 @@ test('candidate playbook preserves stale-salvage operating rules', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('roadmap points to the evaluator RAG prototype and keeps hosted integration open', () => {
|
||||
test('roadmap points to the evaluator RAG prototype and hosted PR check', () => {
|
||||
const roadmap = read('docs/ECC-2.0-GA-ROADMAP.md');
|
||||
|
||||
assert.ok(roadmap.includes('docs/architecture/evaluator-rag-prototype.md'));
|
||||
assert.ok(roadmap.includes('examples/evaluator-rag-prototype/'));
|
||||
assert.ok(roadmap.includes('Local corpus complete; hosted integration remains future'));
|
||||
assert.ok(roadmap.includes('Deterministic hosted PR check, cached output scoring, retrieval planning, judge contract, and gated model execution integrated'));
|
||||
});
|
||||
|
||||
test('billing readiness scenario rejects launch copy overclaims', () => {
|
||||
|
||||
@@ -1143,6 +1143,145 @@ function runTests() {
|
||||
'second subagent edit should pass even on a new file');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// --- Shell-words tokenizer: bypasses the old regex missed ---
|
||||
|
||||
function expectDestructiveDeny(command, label) {
|
||||
clearState();
|
||||
const input = { tool_name: 'Bash', tool_input: { command } };
|
||||
const result = runBashHook(input);
|
||||
assert.strictEqual(result.code, 0, `${label}: exit code should be 0`);
|
||||
const output = parseOutput(result.stdout);
|
||||
assert.ok(output, `${label}: should produce JSON output`);
|
||||
assert.strictEqual(output.hookSpecificOutput.permissionDecision, 'deny', `${label}: should deny`);
|
||||
assert.ok(output.hookSpecificOutput.permissionDecisionReason.includes('Destructive'),
|
||||
`${label}: reason should mention "Destructive"`);
|
||||
}
|
||||
|
||||
function expectAllow(command, label) {
|
||||
clearState();
|
||||
writeState({ checked: ['__bash_session__'], last_active: Date.now() });
|
||||
const input = { tool_name: 'Bash', tool_input: { command } };
|
||||
const result = runBashHook(input);
|
||||
assert.strictEqual(result.code, 0, `${label}: exit code should be 0`);
|
||||
const output = parseOutput(result.stdout);
|
||||
assert.ok(output, `${label}: should produce JSON output`);
|
||||
if (output.hookSpecificOutput) {
|
||||
assert.notStrictEqual(output.hookSpecificOutput.permissionDecision, 'deny', `${label}: should not deny`);
|
||||
} else {
|
||||
assert.strictEqual(output.tool_name, 'Bash', `${label}: pass-through should preserve input`);
|
||||
}
|
||||
}
|
||||
|
||||
if (test('denies short-form git push -f as destructive', () => {
|
||||
expectDestructiveDeny('git push -f origin main', 'git push -f');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git reset --hard even with intervening -c global option', () => {
|
||||
expectDestructiveDeny('git -c core.foo=bar reset --hard', 'git -c ... reset --hard');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies rm -fr (reverse flag order)', () => {
|
||||
expectDestructiveDeny('rm -fr /tmp/junk', 'rm -fr');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies rm -r -f (split flag form)', () => {
|
||||
expectDestructiveDeny('rm -r -f /tmp/junk', 'rm -r -f');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies rm --recursive --force (long flag form)', () => {
|
||||
expectDestructiveDeny('rm --recursive --force /tmp/junk', 'rm --recursive --force');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git reset HEAD --hard (with intervening ref)', () => {
|
||||
expectDestructiveDeny('git reset HEAD --hard', 'git reset HEAD --hard');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git clean -fd (combined force+dirs flag)', () => {
|
||||
expectDestructiveDeny('git clean -fd', 'git clean -fd');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies destructive command in second chained segment', () => {
|
||||
expectDestructiveDeny('echo y | rm -rf /tmp/junk', 'echo y | rm -rf');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies destructive command inside command substitution', () => {
|
||||
expectDestructiveDeny('echo $(rm -rf /tmp/junk)', 'rm -rf inside $()');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies destructive command inside backticks', () => {
|
||||
expectDestructiveDeny('echo `git push -f origin main`', 'git push -f inside backticks');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('allows destructive phrase quoted inside a commit message', () => {
|
||||
expectAllow('git commit -m "fix: rm -rf race in worker"', 'rm -rf in -m');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('allows SQL phrase quoted inside a commit message', () => {
|
||||
expectAllow('git commit -m "docs: explain when drop table is safe"', 'drop table in -m');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('allows git push --force-if-includes as a safety-checked variant', () => {
|
||||
expectAllow('git push --force-with-lease --force-if-includes origin main',
|
||||
'git push --force-if-includes');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// --- Review-round-2 findings ---
|
||||
|
||||
if (test('denies git push --force even with --force-if-includes present', () => {
|
||||
expectDestructiveDeny('git push --force --force-if-includes origin main',
|
||||
'git push --force --force-if-includes');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git push when bare --force is mixed with lease flags', () => {
|
||||
expectDestructiveDeny('git push --force-with-lease --force origin main',
|
||||
'git push --force-with-lease --force');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git push with +refspec prefix (bare branch)', () => {
|
||||
expectDestructiveDeny('git push origin +main', 'git push origin +main');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git push with +refspec prefix (full ref)', () => {
|
||||
expectDestructiveDeny('git push origin +refs/heads/main:refs/heads/main',
|
||||
'git push origin +refs/heads/main:refs/heads/main');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git switch --discard-changes', () => {
|
||||
expectDestructiveDeny('git switch --discard-changes feature',
|
||||
'git switch --discard-changes');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git switch --force', () => {
|
||||
expectDestructiveDeny('git switch --force main', 'git switch --force');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git switch -f short form', () => {
|
||||
expectDestructiveDeny('git switch -f main', 'git switch -f');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies git switch -C force-create', () => {
|
||||
expectDestructiveDeny('git switch -C feature', 'git switch -C');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('still allows plain git switch', () => {
|
||||
expectAllow('git switch feature', 'git switch feature');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies rm -rf nested inside a backtick subshell', () => {
|
||||
expectDestructiveDeny('echo y | `rm -rf /tmp/junk`',
|
||||
'backtick subshell');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies rm -rf nested inside a $(...) subshell', () => {
|
||||
expectDestructiveDeny('echo y | $(rm -rf /tmp/junk)',
|
||||
'dollar-paren subshell');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('denies rm -rf inside double-quoted command substitution', () => {
|
||||
expectDestructiveDeny('echo "$(rm -rf /tmp/junk)"',
|
||||
'double-quoted dollar-paren subshell');
|
||||
})) passed++; else failed++;
|
||||
|
||||
// Cleanup only the temp directory created by this test file.
|
||||
try {
|
||||
if (fs.existsSync(stateDir)) {
|
||||
|
||||
@@ -366,6 +366,66 @@ function runTests() {
|
||||
})) passed++;
|
||||
else failed++;
|
||||
|
||||
// ── hookSpecificOutput JSON on stdout ──
|
||||
// Claude Code 2.1+ drops non-blocking PreToolUse stderr; the suggestion has
|
||||
// to ride on stdout as { hookSpecificOutput: { additionalContext } } to reach
|
||||
// the model. These tests pin that contract.
|
||||
console.log('\nhookSpecificOutput stdout JSON:');
|
||||
|
||||
if (test('emits hookSpecificOutput.additionalContext on stdout at threshold', () => {
|
||||
const { sessionId, counterFile, cleanup } = createCounterContext();
|
||||
cleanup();
|
||||
fs.writeFileSync(counterFile, '49');
|
||||
const result = runCompact({ CLAUDE_SESSION_ID: sessionId });
|
||||
assert.strictEqual(result.code, 0, 'Should exit 0');
|
||||
assert.ok(result.stdout.trim().length > 0, `Expected stdout payload at threshold. Got: "${result.stdout}"`);
|
||||
const parsed = JSON.parse(result.stdout);
|
||||
assert.strictEqual(parsed.hookSpecificOutput.hookEventName, 'PreToolUse',
|
||||
`hookEventName should be PreToolUse. Got: ${JSON.stringify(parsed)}`);
|
||||
assert.ok(parsed.hookSpecificOutput.additionalContext.includes('50 tool calls reached'),
|
||||
`additionalContext should include threshold text. Got: ${parsed.hookSpecificOutput.additionalContext}`);
|
||||
cleanup();
|
||||
})) passed++;
|
||||
else failed++;
|
||||
|
||||
if (test('emits hookSpecificOutput.additionalContext on stdout at +25 interval', () => {
|
||||
const { sessionId, counterFile, cleanup } = createCounterContext();
|
||||
cleanup();
|
||||
// threshold=3, set counter to 27 → next run = 28 → 28-3=25 → interval hit
|
||||
fs.writeFileSync(counterFile, '27');
|
||||
const result = runCompact({ CLAUDE_SESSION_ID: sessionId, COMPACT_THRESHOLD: '3' });
|
||||
assert.strictEqual(result.code, 0, 'Should exit 0');
|
||||
assert.ok(result.stdout.trim().length > 0, `Expected stdout payload at interval. Got: "${result.stdout}"`);
|
||||
const parsed = JSON.parse(result.stdout);
|
||||
assert.strictEqual(parsed.hookSpecificOutput.hookEventName, 'PreToolUse');
|
||||
assert.ok(parsed.hookSpecificOutput.additionalContext.includes('28 tool calls'),
|
||||
`additionalContext should include count. Got: ${parsed.hookSpecificOutput.additionalContext}`);
|
||||
cleanup();
|
||||
})) passed++;
|
||||
else failed++;
|
||||
|
||||
if (test('emits no stdout below threshold (silent)', () => {
|
||||
const { sessionId, cleanup } = createCounterContext();
|
||||
cleanup();
|
||||
const result = runCompact({ CLAUDE_SESSION_ID: sessionId, COMPACT_THRESHOLD: '5' });
|
||||
assert.strictEqual(result.code, 0);
|
||||
assert.strictEqual(result.stdout.trim(), '',
|
||||
`Expected empty stdout below threshold. Got: "${result.stdout}"`);
|
||||
cleanup();
|
||||
})) passed++;
|
||||
else failed++;
|
||||
|
||||
if (test('still writes [StrategicCompact] to stderr (debug log retained)', () => {
|
||||
const { sessionId, counterFile, cleanup } = createCounterContext();
|
||||
cleanup();
|
||||
fs.writeFileSync(counterFile, '49');
|
||||
const result = runCompact({ CLAUDE_SESSION_ID: sessionId });
|
||||
assert.ok(result.stderr.includes('[StrategicCompact]'),
|
||||
`stderr should retain [StrategicCompact] for debug log capture. Got: "${result.stderr}"`);
|
||||
cleanup();
|
||||
})) passed++;
|
||||
else failed++;
|
||||
|
||||
// ── Round 64: default session ID fallback ──
|
||||
console.log('\nDefault session ID fallback (Round 64):');
|
||||
|
||||
|
||||
@@ -143,6 +143,8 @@ function runTests() {
|
||||
const languages = listAvailableLanguages(sourceRoot);
|
||||
|
||||
assert.ok(languages.includes('typescript'));
|
||||
assert.ok(languages.includes('ruby'));
|
||||
assert.ok(languages.includes('rails'));
|
||||
assert.ok(languages.includes('zig'));
|
||||
assert.ok(!languages.includes('common'));
|
||||
assert.deepStrictEqual([...languages].sort(), languages);
|
||||
|
||||
@@ -176,6 +176,8 @@ function runTests() {
|
||||
assert.ok(languages.includes('golang'));
|
||||
assert.ok(languages.includes('kotlin'));
|
||||
assert.ok(languages.includes('rust'));
|
||||
assert.ok(languages.includes('ruby'));
|
||||
assert.ok(languages.includes('rails'));
|
||||
assert.ok(languages.includes('cpp'));
|
||||
assert.ok(languages.includes('c'));
|
||||
assert.ok(languages.includes('csharp'));
|
||||
@@ -432,6 +434,22 @@ function runTests() {
|
||||
'fsharp should resolve to framework-language module');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('resolves ruby and rails legacy compatibility into framework-language and security modules', () => {
|
||||
const selection = resolveLegacyCompatibilitySelection({
|
||||
target: 'cursor',
|
||||
legacyLanguages: ['ruby', 'rails'],
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(selection.canonicalLegacyLanguages, ['ruby', 'ruby']);
|
||||
assert.ok(selection.moduleIds.includes('rules-core'));
|
||||
assert.strictEqual(selection.moduleIds.filter(moduleId => moduleId === 'framework-language').length, 1);
|
||||
assert.strictEqual(selection.moduleIds.filter(moduleId => moduleId === 'security').length, 1);
|
||||
assert.ok(selection.moduleIds.includes('framework-language'),
|
||||
'ruby should resolve to framework-language module');
|
||||
assert.ok(selection.moduleIds.includes('security'),
|
||||
'rails alias should add security guidance for Rails apps');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('keeps antigravity legacy compatibility selections target-safe', () => {
|
||||
const selection = resolveLegacyCompatibilitySelection({
|
||||
target: 'antigravity',
|
||||
|
||||
@@ -236,6 +236,7 @@ function runTests() {
|
||||
assert.ok(components.some(c => c.id === 'lang:python'), 'Should have lang:python');
|
||||
assert.ok(components.some(c => c.id === 'lang:go'), 'Should have lang:go');
|
||||
assert.ok(components.some(c => c.id === 'lang:java'), 'Should have lang:java');
|
||||
assert.ok(components.some(c => c.id === 'lang:ruby'), 'Should have lang:ruby');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('component catalog includes framework: family entries', () => {
|
||||
@@ -244,6 +245,7 @@ function runTests() {
|
||||
assert.ok(components.some(c => c.id === 'framework:nextjs'), 'Should have framework:nextjs');
|
||||
assert.ok(components.some(c => c.id === 'framework:django'), 'Should have framework:django');
|
||||
assert.ok(components.some(c => c.id === 'framework:springboot'), 'Should have framework:springboot');
|
||||
assert.ok(components.some(c => c.id === 'framework:rails'), 'Should have framework:rails');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('component catalog includes capability: family entries', () => {
|
||||
|
||||
@@ -92,7 +92,43 @@ function seedMinimalRepo(rootDir, overrides = {}) {
|
||||
sync: {}
|
||||
}, null, 2),
|
||||
'docs/releases/2.0.0-rc.1/quickstart.md': 'observability-readiness.md',
|
||||
'docs/releases/2.0.0-rc.1/release-notes.md': 'observability-readiness.md'
|
||||
'docs/releases/2.0.0-rc.1/release-notes.md': 'observability-readiness.md',
|
||||
'docs/releases/2.0.0-rc.1/publication-readiness.md': [
|
||||
'Publication Gates',
|
||||
'Required Command Evidence',
|
||||
'Do Not Publish If',
|
||||
'npm dist-tag',
|
||||
'GitGuardian',
|
||||
'Dependabot alerts',
|
||||
'npm audit signatures'
|
||||
].join('\n'),
|
||||
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-13-post-hardening.md': [
|
||||
'npm audit --json',
|
||||
'npm audit signatures',
|
||||
'cargo audit',
|
||||
'Dependabot alert API',
|
||||
'TanStack',
|
||||
'Mini Shai-Hulud',
|
||||
'GitGuardian Security Checks'
|
||||
].join('\n'),
|
||||
'docs/security/supply-chain-incident-response.md': [
|
||||
'TanStack',
|
||||
'Mini Shai-Hulud',
|
||||
'npm audit signatures',
|
||||
'trusted publishing',
|
||||
'pull_request_target',
|
||||
'id-token: write'
|
||||
].join('\n'),
|
||||
'scripts/ci/validate-workflow-security.js': [
|
||||
'persist-credentials: false',
|
||||
'npm audit signatures',
|
||||
'pull_request_target',
|
||||
'id-token: write',
|
||||
'shared cache'
|
||||
].join('\n'),
|
||||
'tests/ci/validate-workflow-security.test.js': 'npm audit signatures persist-credentials: false',
|
||||
'tests/scripts/npm-publish-surface.test.js': 'npm pack --dry-run Python bytecode',
|
||||
'tests/docs/ecc2-release-surface.test.js': 'publication-readiness.md',
|
||||
};
|
||||
|
||||
for (const [relativePath, content] of Object.entries({ ...files, ...overrides })) {
|
||||
@@ -258,6 +294,23 @@ function runTests() {
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('missing release safety evidence fails without disturbing live status checks', () => {
|
||||
const projectRoot = createTempDir('observability-readiness-release-safety-fail-');
|
||||
|
||||
try {
|
||||
seedMinimalRepo(projectRoot, {
|
||||
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-13-post-hardening.md': 'npm audit --json only'
|
||||
});
|
||||
const report = buildReport(projectRoot);
|
||||
|
||||
assert.strictEqual(report.ready, false);
|
||||
assert.ok(report.checks.some(check => check.id === 'release-safety-evidence' && !check.pass));
|
||||
assert.ok(report.checks.some(check => check.id === 'loop-status-live-signal' && check.pass));
|
||||
} finally {
|
||||
cleanup(projectRoot);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log('\nResults:');
|
||||
console.log(` Passed: ${passed}`);
|
||||
console.log(` Failed: ${failed}`);
|
||||
|
||||
Reference in New Issue
Block a user