mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-10 18:23:12 +08:00
Compare commits
3 Commits
codex/may2
...
pr-2006-sc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
958532920f | ||
|
|
f4ff831890 | ||
|
|
b217bbc3fa |
@@ -17,59 +17,11 @@ The May 19 release/growth execution map lives at
|
||||
It is the operator surface for the final ECC 2.0 repo identity, video suite,
|
||||
partner/sponsor funnel, consulting/talk funnel, and social launch plan.
|
||||
|
||||
## 2026-05-20 Delta
|
||||
|
||||
- The tracked platform audit is still green on May 20 with 0 open PRs,
|
||||
0 open issues, 0 discussion maintainer-touch gaps, 0 answerable Q&A gaps,
|
||||
0 conflicting PRs, and 0 blocking dirty files across `affaan-m/ECC`,
|
||||
`affaan-m/agentshield`, `affaan-m/JARVIS`, `ECC-Tools/ECC-Tools`, and
|
||||
`ECC-Tools/ECC-website`.
|
||||
- The new #2015 setup-location Q&A was answered and marked accepted. The
|
||||
answer keeps install guidance conservative: do not install into `C:\`; use a
|
||||
normal workspace, install the `ecc@ecc` Claude plugin once, copy only needed
|
||||
rule folders when using manual rules, and avoid stacking plugin plus full
|
||||
manual install.
|
||||
- ECC-Tools PRs #80-#88 landed the next hosted-platform batch: runtime
|
||||
receipts now require failure reasons; AgentShield fleet approval IDs survive
|
||||
hosted security review and render into comments/check-runs; Linear follow-up
|
||||
sync reuses deterministic external IDs; hosted AgentShield remediation items
|
||||
sync to Linear; hosted job observability events are emitted for queued,
|
||||
completed, blocked, failed, and budget-blocked states; and both hosted job
|
||||
status comments and hosted depth-plan check-runs read back recent
|
||||
observability/budget events. PR #88 adds the authenticated observability API
|
||||
readback for operator dashboards and production smoke tests.
|
||||
- AgentShield PR #94 landed the next cross-harness adapter slice: Zed and
|
||||
VS Code are first-class adapter detections, `.zed/settings.json` and
|
||||
`.zed/tasks.json` are discoverable scan inputs, and `.zed/setup.mjs` now
|
||||
trips the same AI-tool persistence IOC rule as `.vscode/setup.mjs`.
|
||||
- AgentShield PR #95 cleared the remaining default-branch Dependabot alert by
|
||||
moving transitive `brace-expansion` 5.x lockfile entries to `5.0.6`; the
|
||||
post-merge Dependabot open-alert API now returns `[]`, and local
|
||||
`npm audit --audit-level=moderate` returns 0 vulnerabilities.
|
||||
- Linear ITO-54 and the ECC Platform Roadmap now have the May 20 ECC-Tools
|
||||
hosted observability update comments
|
||||
`74dcc101-3be5-4173-be13-62b80d54f569` and
|
||||
`348ea8f5-2a2d-46d9-a0fe-ed99653e7fe5`, after earlier PR #84/#85 comments
|
||||
recorded remediation sync and hosted observability events. PR #88 is recorded
|
||||
in Linear comments `291e2a4b-06e3-4672-a057-cdb141478161` and
|
||||
`b2d35de0-ca49-44cb-982a-ddec229e7691`; AgentShield #94 is recorded in
|
||||
ITO-49 comment `faed69dd-35f5-469d-acb5-ddde6a70d6a1` and project comment
|
||||
`70187c1e-d481-4181-b418-09bd65d54b5e`; AgentShield #95 is recorded in
|
||||
ITO-49 comment `371fc3e4-611f-4d20-a23f-67db1260b418`, ITO-57 comment
|
||||
`bd06e252-15c1-4256-b667-caa3f64f5968`, and project comment
|
||||
`22c2c388-2fd1-4dea-a939-6141f40c9a21`.
|
||||
|
||||
## 2026-05-19 Delta
|
||||
|
||||
- The public repo identity is now `affaan-m/ECC`; release, package, plugin,
|
||||
workflow, and launch-copy surfaces should use that URL for current public
|
||||
links.
|
||||
- The late May 19 queue drain added the deterministic `release:approval-gate`
|
||||
on ECC `main`, merged ECC-Tools billing-announcement redaction hardening, and
|
||||
cleared the JARVIS Dependabot/deploy repair tail. The tracked platform audit
|
||||
is now green with 0 open PRs, 0 open issues, and 0 discussion gaps across all
|
||||
five tracked repos, but release/publication actions remain owner and live-URL
|
||||
gated.
|
||||
- The ECC 2.0 release story should lead with the product shape directly:
|
||||
harness-native operator system, reusable skills/rules/hooks/MCP conventions,
|
||||
`ecc2/` alpha control plane, Hermes as optional operator shell, and ECC Tools
|
||||
@@ -80,15 +32,16 @@ partner/sponsor funnel, consulting/talk funnel, and social launch plan.
|
||||
|
||||
## Current Evidence
|
||||
|
||||
As of 2026-05-20:
|
||||
As of 2026-05-19:
|
||||
|
||||
- GitHub queues are clean across `affaan-m/ECC`,
|
||||
`affaan-m/agentshield`, `affaan-m/JARVIS`, `ECC-Tools/ECC-Tools`, and
|
||||
`ECC-Tools/ECC-website`: the latest `platform-audit` sweep found 0 open PRs,
|
||||
0 open issues, 0 discussion maintainer-touch gaps, 0 answerable Q&A missing
|
||||
accepted answers, and 0 blocking dirty files. The current
|
||||
`scripts/work-items.js list --json` output also reports `totalCount: 0`, so
|
||||
there are no open or blocked local work items in the SQLite bridge.
|
||||
accepted answers, and 0 blocking dirty files when allowing the unrelated
|
||||
local `docs/drafts/` directory. The May 18 sync also refreshed
|
||||
`scripts/work-items.js sync-github` across all five tracked repos, leaving
|
||||
no open or blocked local work items.
|
||||
- Owner-wide queue cleanup is also inside the requested budget:
|
||||
`docs/releases/2.0.0-rc.1/owner-queue-cleanup-2026-05-18.md` records the
|
||||
live `gh search` sweep that closed 24 stale dependency-bot PRs and 72 stale
|
||||
@@ -98,10 +51,9 @@ As of 2026-05-20:
|
||||
now at 0 open PRs and 0 open issues by live `gh search`. Archived repos
|
||||
touched during closure were restored to archived state.
|
||||
- GitHub discussions are current across those tracked repos:
|
||||
`affaan-m/ECC` has 60 total discussions and 0 without
|
||||
`affaan-m/ECC` has 59 total discussions and 0 without
|
||||
maintainer touch after the May 19 #2003 AURA integration proposal was routed
|
||||
as an external-adapter proposal, not core wallet/escrow coupling, and the
|
||||
May 20 #2015 setup-location Q&A was answered and accepted; AgentShield,
|
||||
as an external-adapter proposal, not core wallet/escrow coupling; AgentShield,
|
||||
JARVIS, ECC Tools, and the ECC Tools website have discussions disabled or 0
|
||||
total discussions. `docs/architecture/discussion-response-playbook.md` now
|
||||
supplies the ITO-59 response categories, public templates, security-escalation
|
||||
@@ -133,47 +85,12 @@ As of 2026-05-20:
|
||||
current May 19 queue-zero state, canonical ECC identity merge, release video
|
||||
suite gate, partner/sponsor/talk outreach pack, owner approval packet
|
||||
(`owner-approval-packet-2026-05-19.md`), preview-pack smoke digest
|
||||
`531328aaaa53`, local 2568-test suite, PR #2001 merge and GitHub Actions run
|
||||
`790430aef4a8`, local 2550-test suite, PR #2001 merge and GitHub Actions run
|
||||
`26102500291` success, PR #2002's owner-approval dashboard gate refresh and
|
||||
GitHub Actions run `26103853507`, PR #2004's Linear readiness evidence sync
|
||||
and GitHub Actions run `26105012698`, plus PR #2005's post-PR #2004
|
||||
evidence refresh and GitHub Actions run `26106321921`, PR #2008's supply-chain
|
||||
evidence gate fix and GitHub Actions run `26108473648`, post-PR #2006 main CI
|
||||
run `26109953093`, and PR #2009's project-registry hygiene GitHub Actions run
|
||||
`26111313938`, post-PR #2009 main CI run `26111946778`, post-PR #2011
|
||||
GateGuard main CI run `26113695068`, and post-PR #2013 release-approval-gate
|
||||
main CI run `26128749863`. The late May 19 sync target also includes
|
||||
ECC-Tools PR #79 billing-announcement redaction hardening and JARVIS PR #15
|
||||
/ PR #16 queue/deploy repair, with JARVIS main CI, CodeQL, and Deploy green
|
||||
after the workflow repair. The Linear external project status surface now has
|
||||
both the post-PR #2002 sync document and the late-pass document
|
||||
`ecc-may-19-late-queue-zero-and-release-gate-sync-1c26f65e6b3f`, plus project
|
||||
comment `d42bf0e2-7a8e-4934-9f3f-e281498ee805`. The supply-chain gate now
|
||||
also records the `@types/node@25.7.0` pin and `brace-expansion` lock refresh
|
||||
needed for current npm audit/signature verification.
|
||||
- The May 20 ECC-Tools hosted-platform pass extends that evidence with PR #80
|
||||
through PR #88, all merged after green GitHub Verify/Security Audit/Workers
|
||||
Builds checks. Local validation for the final depth-plan observability slice
|
||||
passed the focused hosted depth-plan route test, the full route suite
|
||||
(89/89), typecheck, lint, full ECC-Tools Vitest suite (683/683), and
|
||||
`git diff --check`. PR #88 additionally exposes authenticated hosted
|
||||
observability readback at `/api/analysis/observability` for operator
|
||||
dashboards and production smoke tests; its local verification passed
|
||||
typecheck, lint, the full ECC-Tools Vitest suite (686/686), and
|
||||
`git diff --check`.
|
||||
- AgentShield PR #94 adds Zed and VS Code to the first-class adapter registry
|
||||
after local verification with typecheck, lint, the focused core scanner/rule
|
||||
tests, full `npm test` (1822 tests), `npm run build`, and `git diff --check`.
|
||||
GitHub checks passed across GitGuardian, scan suite, self-scan,
|
||||
self-scan examples, Node 18/20/22 CI, CodeRabbit, and Cubic after rerunning a
|
||||
transient GitHub artifact-upload failure.
|
||||
- AgentShield PR #95 resolves Dependabot #20 / `GHSA-jxxr-4gwj-5jf2` /
|
||||
`CVE-2026-45149` by updating the vulnerable `brace-expansion` 5.x
|
||||
transitive lockfile entries to `5.0.6`. Local validation passed
|
||||
`npm audit --audit-level=moderate`, typecheck, lint, full `npm test`
|
||||
(1822 tests), build, and whitespace checks; GitHub checks passed across
|
||||
Verify Node 18/20/22, self-scan, self-scan examples, Test GitHub Action,
|
||||
GitGuardian, CodeRabbit, and Cubic.
|
||||
GitHub Actions run `26103853507`, plus PR #2004's Linear readiness evidence
|
||||
sync and GitHub Actions run `26105012698`. The May 19 Linear sync document
|
||||
remains the current external project status surface, and the May 18 evidence
|
||||
remains the detailed supply-chain and publication-path snapshot.
|
||||
- `docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-19.md`
|
||||
regenerates the ITO-44 prompt-to-artifact dashboard from live platform audit
|
||||
evidence: PR queue, issue queue, discussion queue, local worktree gate,
|
||||
@@ -201,7 +118,7 @@ As of 2026-05-20:
|
||||
finding evidence paths, ECC-Tools #78 harness policy-route linking, PR #1947
|
||||
supply-chain protection, and May 16 release-evidence
|
||||
refresh.
|
||||
- `npm run harness:audit -- --format json` reports 80/80 on current `main`.
|
||||
- `npm run harness:audit -- --format json` reports 70/70 on current `main`.
|
||||
- `npm run observability:ready` reports 21/21 readiness on current `main`,
|
||||
including the GitHub/Linear/handoff/roadmap progress-sync contract.
|
||||
- GitHub CI run `26017368895` completed successfully for
|
||||
@@ -463,20 +380,13 @@ As of 2026-05-20:
|
||||
`brace-expansion@5.0.6` and fixed Dependabot alert 44 for CVE-2026-45149;
|
||||
GitHub API reported `state: fixed` at `2026-05-18T19:10:15Z` and current-head
|
||||
CI `26054671308` passed.
|
||||
- ECC-Tools PR #89 merged as `512bca6b99cdaa67058a6aa9a4e7e7f0b1d9873a`
|
||||
and adds
|
||||
`npm run billing:kv-readback -- --select-ready-target --require-ready` so
|
||||
operators can prove a ready Marketplace Pro account without passing or
|
||||
printing the login. The 2026-05-20 production Wrangler OAuth readback found
|
||||
ready-like Marketplace Pro records with webhook provenance and 0 parse
|
||||
failures. The selected target report printed only a stable fingerprint,
|
||||
confirmed both key families, `marketplace` source, `pro` tier, seat ready,
|
||||
webhook evidence ready, automatic overage disabled, and 0 blockers. The old
|
||||
"no Marketplace-managed Pro target billing-state" blocker is cleared.
|
||||
Native-payments copy remains blocked until the local/internal
|
||||
`INTERNAL_API_SECRET` bearer-token path is available and the live
|
||||
`billing:announcement-gate -- --account <target>` call passes. Linear comment
|
||||
`f14ed2fe-a219-470c-8119-63429e197027` records the redacted readback counts.
|
||||
- The latest ITO-61 readback retry narrowed the blocker: Wrangler OAuth now
|
||||
works, the safe aggregate readback has 0 parse failures and still reports 0
|
||||
Marketplace Pro records, and `billing:announcement-gate -- --preflight` is
|
||||
missing the target Marketplace account plus `INTERNAL_API_SECRET`.
|
||||
Native-payments copy remains blocked until the target Pro readback and live
|
||||
announcement gate pass. Linear comment
|
||||
`6904e4fb-bec7-4787-90e2-759f077a628c` records the redacted readback counts.
|
||||
- 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
|
||||
@@ -735,44 +645,6 @@ As of 2026-05-20:
|
||||
fleet summaries are collected as harness evidence, target paths are mapped to
|
||||
Claude, Codex, OpenCode, MCP, plugin, and cross-harness owners, and routed
|
||||
findings carry source evidence paths for operator review.
|
||||
- ECC-Tools PR #79 merged as `67ee247ae1b7b50ecc1261ed5d62d65cc8390da8`
|
||||
and redacts billing announcement gate account output: the billing preflight
|
||||
and live readback now print stable account fingerprints and sanitized
|
||||
readiness booleans instead of raw account logins or KV key names.
|
||||
- ECC-Tools PR #80 merged as `4efc8cc858022f84c844690f3298633b081c4398`
|
||||
and requires runtime receipt failure reasons before harness runtime receipts
|
||||
can count as hosted observability evidence.
|
||||
- ECC-Tools PR #81 merged as `1fbf635f492284f75ba7166c029c39eb8cc15794`
|
||||
and preserves AgentShield fleet approval IDs through hosted security review
|
||||
so policy-promotion follow-ups keep owner-review identity stable.
|
||||
- ECC-Tools PR #82 merged as `7a7b4d096a176ae80b3a2076c09d45601e36013a`
|
||||
and renders AgentShield fleet approval IDs in hosted comments and check-runs,
|
||||
giving operators a direct bridge from hosted security review back to
|
||||
AgentShield policy-promotion review items.
|
||||
- ECC-Tools PR #83 merged as `b6b107f33961bef18a85fb619f3a976eb5d752dd`
|
||||
and makes Linear follow-up sync reuse deterministic external IDs before title
|
||||
fallback, preventing duplicate deferred backlog issues during repeated
|
||||
`/ecc-tools followups sync-linear` runs.
|
||||
- ECC-Tools PR #84 merged as `73bac7058071c55cb30c6b8ac6db779b3660c02c`
|
||||
and syncs hosted AgentShield remediation items to Linear when the workspace
|
||||
token/team are configured; hosted result comments now include created/reused
|
||||
Linear remediation links.
|
||||
- ECC-Tools PR #85 merged as `1637e0f2bfa0a889387f2c20675680ccc5528123`
|
||||
and emits hosted job observability events for queued, completed, blocked,
|
||||
failed, and budget-blocked states into `ANALYSIS_CACHE`, including budget
|
||||
snapshots and result counts.
|
||||
- ECC-Tools PR #86 merged as `5a9e94d3ff860307c3e7fd9fd065f0de2bd633dd`
|
||||
and reads recent hosted observability events in
|
||||
`/ecc-tools analyze --job status`, so status comments show budget snapshots,
|
||||
blocked results, and budget-blocked outcomes alongside latest job runs.
|
||||
- ECC-Tools PR #87 merged as `508fbc02b63cf1fcb5af2f3624608fa66e53b5d4`
|
||||
and adds the same hosted observability readback to hosted depth-plan
|
||||
check-runs, keeping the PR check surface aligned with status comments.
|
||||
- ECC-Tools PR #88 merged as `c836ac3fb24ed7e2ae38cd61e41c9651ac9c00f8`
|
||||
and exposes authenticated hosted observability API readback at
|
||||
`/api/analysis/observability`, summarizing recent hosted events by event type
|
||||
and job while skipping malformed stale KV records. The deployment runbook now
|
||||
includes the production smoke command for operator/dashboard readback.
|
||||
- AgentShield PR #90 merged as `6d1c57c92000541d65a3b6bc366f0322d7d0dacc`
|
||||
and adds durable fleet `reviewItems`: `agentshield evidence-pack fleet --json`
|
||||
now returns owner-ready review items with route, severity, repository/target
|
||||
@@ -789,16 +661,6 @@ As of 2026-05-20:
|
||||
policy SHA-256 digest, rejects tampered policy JSON, requires explicit pack
|
||||
selection for multi-pack manifests, and supports dry-run JSON review before
|
||||
writing the active `.agentshield/policy.json`.
|
||||
- AgentShield PR #94 merged as `4caee27acfadb50a4cd024e738b5c3cbd4b0bb03`
|
||||
and adds editor-native adapter coverage for Zed and VS Code. Zed
|
||||
`.zed/settings.json`, `.zed/tasks.json`, and `.zed` hook-code files are now
|
||||
scan inputs, adapter reports expose Zed MCP/tool-permission/task metadata and
|
||||
VS Code workspace/task/extension metadata, and `.zed/setup.mjs` is covered by
|
||||
the AI-tool persistence IOC rule.
|
||||
- AgentShield PR #95 merged as `25d91f0002214c408da4ceaac7def20bad40ca10`
|
||||
and clears the `brace-expansion` Dependabot alert. The lockfile now resolves
|
||||
the vulnerable transitive 5.x copies to `5.0.6`; the remaining 1.x copy is
|
||||
outside the advisory range.
|
||||
- AgentShield main commit `87aec47fb55d04ea28d494852d4f664c268c5601`
|
||||
extends policy promotion with durable `reviewItems` for manifest digest
|
||||
evidence, policy-owner approval, protected rollout PR handoff, and runtime
|
||||
@@ -864,7 +726,7 @@ As of 2026-05-20:
|
||||
|
||||
- Keep public PRs and issues below 20, with zero as the preferred release-lane
|
||||
target.
|
||||
- Maintain 80/80 harness audit and 21/21 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
|
||||
@@ -872,9 +734,7 @@ As of 2026-05-20:
|
||||
- Do not treat closed stale PRs as discarded. Pair each cleanup batch with a
|
||||
salvage pass: inspect the closed diffs, port useful compatible work on
|
||||
maintainer-owned branches, and credit the source PR.
|
||||
- Use Linear project documents/comments for project-level updates because
|
||||
project status updates are disabled in this workspace; create or update
|
||||
issues when a lane needs a durable execution owner.
|
||||
- Do not create new Linear issues until the active issue limit is cleared.
|
||||
|
||||
## Prompt-To-Artifact Execution Checklist
|
||||
|
||||
@@ -883,23 +743,23 @@ 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 `ECC`, AgentShield, JARVIS, `ECC-Tools/ECC-Tools`, and `ECC-Tools/ECC-website` on the late 2026-05-19 platform audit after merging ECC PR #2013, ECC-Tools PR #79, JARVIS PR #15, and JARVIS PR #16 | Complete |
|
||||
| Keep public PRs below 20 | Repo-family PR recheck | 0 open PRs across `ECC`, AgentShield, JARVIS, `ECC-Tools/ECC-Tools`, and `ECC-Tools/ECC-website` on 2026-05-19 after merging PR #2004 and refreshing platform audit evidence | Complete |
|
||||
| Keep public issues below 20 | Repo-family issue recheck | 0 open issues across `ECC`, AgentShield, JARVIS, `ECC-Tools/ECC-Tools`, and `ECC-Tools/ECC-website` on 2026-05-19 after the live platform audit refresh | Complete |
|
||||
| Manage repository discussions | Repo-family discussion recheck plus response playbook | Platform audit reports 0 discussion maintainer-touch gaps and 0 answerable Q&A missing accepted answers; trunk has 59 total discussions after #2003 was routed with a maintainer response; `docs/architecture/discussion-response-playbook.md` distinguishes support, maintainer coordination, stale/concluded, release, informational, and security-sensitive response paths | Complete |
|
||||
| Manage PR discussions | PR review/comment closure plus merge/close state | ECC #1990-#2013 merged through the harness audit, canonical identity, release video suite, growth outreach, evidence refresh, visual QA, suite-count, owner-approval packet, owner-approval dashboard gate, Linear readiness evidence, supply-chain evidence gate, per-project Claude Code adapter, continuous-learning project-registry hygiene, GateGuard quoted git introspection, and deterministic release-approval gate batch; ECC-Tools #79 and JARVIS #15/#16 also merged; no open tracked PRs remain | Complete |
|
||||
| Manage PR discussions | PR review/comment closure plus merge/close state | ECC #1990-#2004 merged through the harness audit, canonical identity, release video suite, growth outreach, evidence refresh, visual QA, suite-count, owner-approval packet, owner-approval dashboard gate, and Linear readiness evidence batch; no open tracked PRs remain | Complete |
|
||||
| Salvage useful stale work | `docs/stale-pr-salvage-ledger.md` plus `docs/legacy-artifact-inventory.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; #1687, #1609, #1563, #1564, and #1565 localization tails are attached to Linear ITO-55 for language-owner review and no automatic import remains release-blocking | Complete; repeat legacy scan before release |
|
||||
| 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 19 evidence records queue-zero state, canonical ECC identity, release video suite, growth outreach pack, owner approval packet, local 2568-test suite, PR #2001 merge and GitHub Actions run `26102500291`, PR #2002 owner-approval dashboard gate refresh and GitHub Actions run `26103853507`, PR #2004 Linear readiness evidence sync and GitHub Actions run `26105012698`, PR #2008 supply-chain evidence gate CI run `26108473648`, post-PR #2006 main CI run `26109953093`, PR #2009 project-registry hygiene GitHub Actions run `26111313938`, post-PR #2009 main CI run `26111946778`, post-PR #2011 GateGuard main CI run `26113695068`, post-PR #2013 release-approval main CI run `26128749863`, May 19 operator dashboard, `owner-approval-packet-2026-05-19.md`, `release-approval-gate.js`, and preview-pack smoke digest `531328aaaa53` | Needs final release approval |
|
||||
| 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 19 evidence records queue-zero state, canonical ECC identity, release video suite, growth outreach pack, owner approval packet, local 2550-test suite, PR #2001 merge and GitHub Actions run `26102500291`, PR #2002 owner-approval dashboard gate refresh and GitHub Actions run `26103853507`, PR #2004 Linear readiness evidence sync and GitHub Actions run `26105012698`, May 19 operator dashboard, `owner-approval-packet-2026-05-19.md`, and preview-pack smoke digest `790430aef4a8` | Needs final 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, partner/sponsor/talk pack | Draft launch collateral and approval-gated outreach copy exist under rc.1 release docs | Needs URL-backed refresh and human approval before posting or sending |
|
||||
| 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, editor-native Zed/VS Code adapter coverage, Dependabot alert closure, enterprise research roadmap, supply-chain hardened release path, CI-safe baseline fingerprints, corpus accuracy recommendations, remediation workflow phases, env proxy hijack corpus coverage, Mini Shai-Hulud full-campaign package IOCs, CI-provenance evidence packs, plugin-cache runtime-confidence triage, evidence-pack consumer readback, fleet-level evidence-pack routing, fleet review items, fleet review ticket payloads, checksum-backed policy export, checksum-verified policy promotion, policy promotion review items, package-manager hardening drift detection, npm age-gate guidance correction, workflow action-runtime pin refresh, package-manager hardening Action outputs, policy-promotion Action outputs, ECC-Tools hosted consumption of promotion Action outputs, ECC-Tools operator-visible promotion output values, and ECC-Tools hosted promotion judge audit traces | PRs #53, #55-#64, #67-#69, #78-#92, #94, and #95 landed with test evidence, ECC-Tools #76 consumes the fleet-summary output in hosted security review, #77 surfaces source evidence paths in hosted finding output, and #78 links fleet routes to harness owner review; AgentShield #91 adds `agentshield policy export` bundles for branch-protection review and downstream promotion; AgentShield #92 adds `agentshield policy promote` with digest verification, tamper rejection, explicit pack selection, dry-run review, and JSON output before writing active policy; AgentShield #94 adds Zed/VS Code adapter detection, `.zed/settings.json` and `.zed/tasks.json` scan discovery, and `.zed/setup.mjs` AI-tool persistence IOC coverage; AgentShield #95 clears the `brace-expansion` Dependabot alert with a patched lockfile and 0 open Dependabot alerts after merge; AgentShield commit `87aec47` adds `reviewItems` for digest evidence, owner review, protected rollout PR handoff, and runtime smoke testing with green local and remote CI; AgentShield commit `28d08c7` adds package-manager hardening drift detection for plaintext registry credentials, lifecycle-script enablement, and weak pnpm/Yarn release-age cooldowns with green local and remote CI; AgentShield commit `659f569` refreshes all workflow action runtime pins to SHA-pinned checkout v6.0.2 and setup-node v6.4.0 with green remote CI and no remaining action-runtime deprecation annotation; AgentShield commit `ee585cd` corrects npm release-age guidance by flagging unsupported npm age keys and keeping enforceable cooldown findings on pnpm/Yarn with green local and remote CI; AgentShield commit `1124535` exposes package-manager hardening status/count outputs and a redacted job-summary section for registry credentials, lifecycle scripts, and release-age gates with green local and remote CI; AgentShield commit `1593925` exposes policy-promotion status/count/digest outputs plus job-summary review items for owner approval, protected rollout, and runtime smoke, and marks runtime smoke verified when the same Action job scans with the promoted policy; AgentShield commit `840952a` adds Linear/operator-ready fleet review ticket payloads and expands current Mini Shai-Hulud IOC breadcrumbs with green local and remote CI; ECC-Tools commit `8658951` routes those policy-promotion Action outputs into hosted security review findings and Hosted Promotion Readiness scoring; ECC-Tools commit `16c537f` renders policy-promotion status, pack, review item count, action-required count, and digest in hosted security job comments/check-runs; ECC-Tools commit `05d4e82` renders hosted promotion judge request fingerprints and allowed-citation counts without raw provider output; native PDF export deferred in favor of self-contained HTML plus print-to-PDF until explicit enterprise demand appears; `docs/architecture/agentshield-enterprise-research-roadmap.md` now has baseline drift, evidence-pack bundle, redaction, adapter-registry, supply-chain hardening, hashed baseline fingerprints, corpus accuracy recommendation, remediation workflow, env proxy hijack corpus, Mini Shai-Hulud full-campaign package-table, `ci-context.json` provenance, `plugin-cache` confidence, `evidence-pack inspect` readback, `evidence-pack fleet` routing, fleet `reviewItems`, fleet review ticket payloads, policy export, policy promotion, policy promotion `reviewItems`, package-manager hardening Action outputs, policy-promotion Action outputs, hosted consumption of promotion Action outputs, operator-visible promotion output values, hosted promotion judge audit traces, editor-native adapter coverage, and Dependabot closure landed | Next workflow automation should deepen live operator approval/readback after Marketplace/payment gates |
|
||||
| ECC Tools next-level app | Billing audit, PR checks, deep analyzer, sync backlog, evaluator/RAG corpus, hosted promotion judge audit trace, native-payments readback, ready Marketplace Pro target selection, hosted observability, AgentShield fleet-summary hosted routing, hosted finding evidence paths, harness-route policy linking, policy-promotion Action-output hosted telemetry, and operator-visible promotion output values | PRs #26-#43 plus #53-#89 landed with test evidence across hosted analysis, hosted promotion readiness, model-judge execution, native-payments announcement gating, AgentShield evidence consumption, hosted remediation/Linear sync, hosted observability readback, and ready Marketplace Pro target selection; ECC-Tools #89 merged as `512bca6` after Verify, Security Audit, and Workers Builds passed, and the 2026-05-20 production Wrangler OAuth readback found ready-like Marketplace Pro records with webhook provenance, selected a target with both key families, and reported 0 blockers without printing the login | Next work is obtain or rotate the local/internal `INTERNAL_API_SECRET` bearer-token path, then run the live billing announcement gate for the selected Marketplace Pro target before publishing native-payments copy |
|
||||
| AgentShield enterprise iteration | Policy gates, SARIF, packs, provenance, corpus, HTML reports, exception lifecycle audit, baseline drift Action/CLI surfaces, evidence-pack redaction, harness adapter registry, enterprise research roadmap, supply-chain hardened release path, CI-safe baseline fingerprints, corpus accuracy recommendations, remediation workflow phases, env proxy hijack corpus coverage, Mini Shai-Hulud full-campaign package IOCs, CI-provenance evidence packs, plugin-cache runtime-confidence triage, evidence-pack consumer readback, fleet-level evidence-pack routing, fleet review items, fleet review ticket payloads, checksum-backed policy export, checksum-verified policy promotion, policy promotion review items, package-manager hardening drift detection, npm age-gate guidance correction, workflow action-runtime pin refresh, package-manager hardening Action outputs, policy-promotion Action outputs, ECC-Tools hosted consumption of promotion Action outputs, ECC-Tools operator-visible promotion output values, and ECC-Tools hosted promotion judge audit traces | PRs #53, #55-#64, #67-#69, and #78-#92 landed with test evidence, ECC-Tools #76 consumes the fleet-summary output in hosted security review, #77 surfaces source evidence paths in hosted finding output, and #78 links fleet routes to harness owner review; AgentShield #91 adds `agentshield policy export` bundles for branch-protection review and downstream promotion; AgentShield #92 adds `agentshield policy promote` with digest verification, tamper rejection, explicit pack selection, dry-run review, and JSON output before writing active policy; AgentShield commit `87aec47` adds `reviewItems` for digest evidence, owner review, protected rollout PR handoff, and runtime smoke testing with green local and remote CI; AgentShield commit `28d08c7` adds package-manager hardening drift detection for plaintext registry credentials, lifecycle-script enablement, and weak pnpm/Yarn release-age cooldowns with green local and remote CI; AgentShield commit `659f569` refreshes all workflow action runtime pins to SHA-pinned checkout v6.0.2 and setup-node v6.4.0 with green remote CI and no remaining action-runtime deprecation annotation; AgentShield commit `ee585cd` corrects npm release-age guidance by flagging unsupported npm age keys and keeping enforceable cooldown findings on pnpm/Yarn with green local and remote CI; AgentShield commit `1124535` exposes package-manager hardening status/count outputs and a redacted job-summary section for registry credentials, lifecycle scripts, and release-age gates with green local and remote CI; AgentShield commit `1593925` exposes policy-promotion status/count/digest outputs plus job-summary review items for owner approval, protected rollout, and runtime smoke, and marks runtime smoke verified when the same Action job scans with the promoted policy; AgentShield commit `840952a` adds Linear/operator-ready fleet review ticket payloads and expands current Mini Shai-Hulud IOC breadcrumbs with green local and remote CI; ECC-Tools commit `8658951` routes those policy-promotion Action outputs into hosted security review findings and Hosted Promotion Readiness scoring; ECC-Tools commit `16c537f` renders policy-promotion status, pack, review item count, action-required count, and digest in hosted security job comments/check-runs; ECC-Tools commit `05d4e82` renders hosted promotion judge request fingerprints and allowed-citation counts without raw provider output; native PDF export deferred in favor of self-contained HTML plus print-to-PDF until explicit enterprise demand appears; `docs/architecture/agentshield-enterprise-research-roadmap.md` now has baseline drift, evidence-pack bundle, redaction, adapter-registry, supply-chain hardening, hashed baseline fingerprints, corpus accuracy recommendation, remediation workflow, env proxy hijack corpus, Mini Shai-Hulud full-campaign package-table, `ci-context.json` provenance, `plugin-cache` confidence, `evidence-pack inspect` readback, `evidence-pack fleet` routing, fleet `reviewItems`, fleet review ticket payloads, policy export, policy promotion, policy promotion `reviewItems`, package-manager hardening Action outputs, policy-promotion Action outputs, hosted consumption of promotion Action outputs, operator-visible promotion output values, and hosted promotion judge audit traces landed | Next workflow automation should deepen live operator approval/readback after Marketplace/payment gates |
|
||||
| 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, hosted promotion judge audit trace, payment-announcement readiness, billing announcement preflight, aggregate production billing KV readback, Marketplace webhook provenance, target-account billing readback, Marketplace-source provenance counts, AgentShield fleet-summary hosted routing, hosted finding evidence paths, harness-route policy linking, policy-promotion Action-output hosted telemetry, and operator-visible promotion output values | PRs #26-#43 plus #53-#78 landed with test evidence, including AgentShield evidence-pack gap routing, canonical bundle recognition, supply-chain signature gates, PR draft follow-up Linear tracking, evidence-backed/deep-ready repository classification, the `/api/analysis/depth-plan` hosted job plan, `/api/analysis/jobs/ci-diagnostics`, `/api/analysis/jobs/security-evidence-review`, `/api/analysis/jobs/harness-compatibility-audit`, `/api/analysis/jobs/reference-set-evaluation`, `/api/analysis/jobs/ai-routing-cost-review`, `/api/analysis/jobs/team-backlog-routing`, the `ECC Tools / Hosted Depth Plan` check-run, `/ecc-tools analyze --job ...` PR-comment dispatch, non-blocking per-hosted-job result check-runs backed by 30-day result cache records, `/ecc-tools analyze --job status` cache lookup, cache-aware next-job recommendations in the depth-plan check-run, the `ECC Tools / Hosted Promotion Readiness` corpus-backed PR check-run, deterministic hosted-output scoring against cached completed job artifacts/findings, ranked retrieval/model-prompt planning, the fail-closed `hosted-promotion-judge.v1` request contract, opt-in live model-judge execution behind hosted evidence, entitlement, budget, provider, executor, strict JSON, and citation gates, hosted promotion judge request fingerprints plus allowed-citation audit trails, a fail-closed `/api/billing/readiness` `announcementGate` for native GitHub payments claims, `npm run billing:announcement-gate` plus `--preflight` as the non-secret operator verifier, hosted security findings for AgentShield fleet summaries, an `Evidence` column in hosted finding comments/check-runs, hosted harness findings that route AgentShield fleet target paths to harness owners, ECC-Tools commit `8658951` routing AgentShield policy-promotion Action outputs into hosted security review and promotion-readiness scoring, ECC-Tools commit `16c537f` rendering policy-promotion status/pack/count/digest values directly in hosted security job comments/check-runs, ECC-Tools commit `05d4e82` rendering model-judge audit traces without exposing raw provider output, ECC-Tools commit `91a441b` adding the safe billing announcement preflight path, ECC-Tools commit `eb69412` recording the initial production readback state, ECC-Tools commit `95d0bec` adding `npm run billing:kv-readback` with aggregate account-billing and billing-state records but 0 Marketplace Pro billing-state records, ECC-Tools commit `2859678` requiring webhook-derived Marketplace provenance before announcement readiness, ECC-Tools commit `42653f9` adding Wrangler OAuth readback, ECC-Tools commit `632e059` adding sanitized target-account readback that requires both target key families before `--require-ready` can pass, and ECC-Tools commit `d5f60db` adding sanitized Marketplace plan/action provenance counts; the latest 2026-05-18 live Wrangler OAuth recheck found 256 account-billing records, 256 billing-state records, 197 Marketplace-source records, 4 Marketplace webhook-provenance records, all `Open Source`, and 0 Marketplace Pro records, then updated Linear ITO-61 with the data/provisioning blocker | Next work is create or verify Marketplace-managed Pro target billing-state with webhook provenance, configure target account plus `INTERNAL_API_SECRET`, then run `billing:kv-readback -- --wrangler --wrangler-bin ./node_modules/.bin/wrangler --account <github-login> --require-ready`, followed by the live announcement gate |
|
||||
| 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 document/comments plus repo mirror | Repo mirror exists and issue creation works again; the May 19 sync adds post-PR #2002 document `ecc-may-19-post-pr-2002-sync-64cef8f668e0`, project comment `a6411e3a-8c8e-4a58-adba-687e77d4c543`, ITO-44/47/48/49/51/54/56 issue comments, and In Progress state for ITO-47, ITO-48, ITO-49, ITO-51, ITO-54, and ITO-56; the late-pass batch adds document `ecc-may-19-late-queue-zero-and-release-gate-sync-1c26f65e6b3f`, project comment `d42bf0e2-7a8e-4934-9f3f-e281498ee805`, and ITO-44/50/54/56/61 comments for PR #2013, ECC-Tools #79, and JARVIS #15/#16 because project status updates are disabled in the workspace | Needs recurring document/comment updates after each significant merge batch |
|
||||
| Linear roadmap is detailed | Linear project status plus repo mirror | Repo mirror exists and issue creation works again; the May 19 sync adds post-PR #2002 document `ecc-may-19-post-pr-2002-sync-64cef8f668e0`, project comment `a6411e3a-8c8e-4a58-adba-687e77d4c543`, ITO-44/47/48/49/51/54/56 issue comments, and In Progress state for ITO-47, ITO-48, ITO-49, ITO-51, ITO-54, and ITO-56; PR #2004 mirrors that sync into the repo evidence set | Needs recurring status updates after each significant merge batch |
|
||||
| Flow separation and progress tracking | Flow lanes with owner artifacts and update cadence | This roadmap defines lanes below and `docs/architecture/progress-sync-contract.md` makes GitHub/Linear/handoff/roadmap sync part of the readiness gate | Active |
|
||||
| Realtime Linear sync | Project documents/comments plus issue comments for lane updates | 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; May 18 and May 19 live connector comments were posted to the ECC platform project and lane issues after project status updates returned disabled | Needs workspace config/product rollout for hosted issue sync |
|
||||
| Realtime Linear sync | Project comments while issue/status capacity 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; May 18 live connector comments were posted to ITO-57 and the ECC platform project after project status updates returned disabled | Needs workspace capacity/config rollout for productized issue sync |
|
||||
| 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 |
|
||||
|
||||
@@ -917,7 +777,7 @@ repo evidence and merge commits.
|
||||
| Release and publication | rc.1 release docs, publication readiness doc | Naming matrix and plugin submission/contact checklist | Before any tag |
|
||||
| Harness OS core | Audit, adapter matrix, observability docs, `ecc2/` | HUD/session-control acceptance spec | Weekly until GA |
|
||||
| Evaluation and RAG | Reference-set validation, harness audit, traces, ECC-Tools corpus | Read-only evaluator/RAG prototype plus stale-salvage, billing-readiness, CI-failure-diagnosis, harness-config-quality, AgentShield policy-exception, skill-quality evidence, deep-analyzer evidence, and RAG/evaluator comparison fixtures; ECC-Tools #68 publishes the corpus as a hosted promotion readiness check-run, #69 scores cached hosted job outputs against the same corpus, #70 emits ranked retrieval candidates plus a model prompt seed, #71 adds a fail-closed hosted model-judge request contract, and #72 executes that judge only when explicitly enabled and backed by hosted retrieval citations; ECC-Tools `16c537f` surfaces policy-promotion Action output values in hosted security comments/checks; ECC-Tools `05d4e82` adds hosted model-judge audit traces with request fingerprints and allowed-citation counts | Marketplace Pro billing-state verification with webhook provenance |
|
||||
| AgentShield enterprise | AgentShield PR evidence and roadmap notes | Fleet routing landed in #89 after evidence-pack inspect/readback shipped in #88; #90 emits fleet `reviewItems`; #91 exports checksum-backed policy bundles; #92 promotes checksum-verified policies from those bundles into active policy files; #94 adds Zed and VS Code adapter detection, Zed project scan discovery, and `.zed/setup.mjs` persistence IOC coverage; #95 closes the `brace-expansion` Dependabot alert with 0 open alerts after merge; AgentShield `87aec47` adds policy promotion `reviewItems`; `28d08c7` adds package-manager hardening drift detection; `659f569` refreshes workflow action runtime pins; `ee585cd` corrects unsupported npm release-age guidance and keeps enforceable cooldown findings on pnpm/Yarn; `1124535` exposes package-manager hardening Action outputs for CI/hosted routing; `1593925` exposes policy-promotion Action outputs and runtime-smoke job-summary evidence; `840952a` adds fleet review ticket payloads and current Mini Shai-Hulud IOC breadcrumbs; ECC-Tools #76 consumes fleet summaries, #77 surfaces source evidence paths in hosted findings, #78 links fleet routes to harness owners, ECC-Tools `8658951` consumes policy-promotion Action outputs, and ECC-Tools `16c537f` renders operator-visible output values | Deepen live operator approval/readback after Marketplace/payment gates |
|
||||
| AgentShield enterprise | AgentShield PR evidence and roadmap notes | Fleet routing landed in #89 after evidence-pack inspect/readback shipped in #88; #90 emits fleet `reviewItems`; #91 exports checksum-backed policy bundles; #92 promotes checksum-verified policies from those bundles into active policy files; AgentShield `87aec47` adds policy promotion `reviewItems`; `28d08c7` adds package-manager hardening drift detection; `659f569` refreshes workflow action runtime pins; `ee585cd` corrects unsupported npm release-age guidance and keeps enforceable cooldown findings on pnpm/Yarn; `1124535` exposes package-manager hardening Action outputs for CI/hosted routing; `1593925` exposes policy-promotion Action outputs and runtime-smoke job-summary evidence; `840952a` adds fleet review ticket payloads and current Mini Shai-Hulud IOC breadcrumbs; ECC-Tools #76 consumes fleet summaries, #77 surfaces source evidence paths in hosted findings, #78 links fleet routes to harness owners, ECC-Tools `8658951` consumes policy-promotion Action outputs, and ECC-Tools `16c537f` renders operator-visible output values | Deepen live operator approval/readback after Marketplace/payment gates |
|
||||
| ECC Tools app | ECC-Tools PR evidence, billing audit, risk taxonomy, evaluator/RAG corpus | ECC-Tools #53 published the supply-chain workflow hardening branch, #54 tracks copy-ready PR drafts in the Linear/project backlog, #55 classifies analysis-depth readiness, #56 exposes the hosted execution plan, #57 executes the first hosted CI diagnostics job, #58 executes the hosted security evidence review job, #59 executes the hosted harness compatibility audit, #60 executes the hosted reference-set evaluation, #61 executes the hosted AI routing/cost review, #62 executes hosted team backlog routing, #63 publishes the hosted depth-plan check-run, #64 dispatches hosted jobs from PR comments, #65 persists hosted result history/check-runs, #66 exposes hosted job status from PR comments, #67 makes depth-plan recommendations cache-aware, #68 publishes hosted promotion readiness from the evaluator/RAG corpus, #69 scores cached hosted job outputs against that corpus, #70 emits ranked retrieval candidates plus a model prompt seed, #71 emits the gated `hosted-promotion-judge.v1` contract without live model calls, #72 adds opt-in live model-judge execution behind hosted-evidence and strict JSON/citation gates, #73 adds a fail-closed native-payments `announcementGate` to billing readiness, #74 adds `npm run billing:announcement-gate` for operator verification, #75 tightens the billing announcement gate for live Marketplace readback, #76 routes AgentShield fleet-summary evidence into hosted security findings, #77 adds source evidence paths to hosted finding output, #78 links AgentShield fleet target paths to hosted harness owner findings, `8658951` routes AgentShield policy-promotion Action outputs into hosted security review and promotion readiness, `16c537f` renders policy-promotion status/pack/count/digest values in hosted security comments/checks, `05d4e82` renders hosted promotion judge request fingerprints plus allowed-citation audit traces, `91a441b` adds billing announcement preflight output for required readback inputs, `eb69412` records the initial production readback state, `95d0bec` adds aggregate `billing:kv-readback` evidence, `2859678` requires Marketplace webhook provenance in billing readiness, `42653f9` adds Wrangler OAuth readback with live aggregate production counts, and `632e059` adds sanitized target-account billing readback for the exact Marketplace test account | Create or verify Marketplace-managed Pro target billing-state with webhook provenance, then live target readback and announcement gate |
|
||||
| Linear progress | Linear project status updates, `docs/architecture/progress-sync-contract.md`, generated `operator:dashboard` output, and this mirror | Status update with queue/evidence/missing gates | Every significant merge batch |
|
||||
|
||||
@@ -978,7 +838,7 @@ Acceptance:
|
||||
Zed-adjacent surfaces, dmux, Orca, Superset, Ghast, and terminal-only use.
|
||||
- Each adapter has supported assets, unsupported surfaces, install path,
|
||||
verification command, and risk notes.
|
||||
- Harness audit remains 80/80 and gains a public onramp that explains how teams
|
||||
- Harness audit remains 70/70 and gains a public onramp that explains how teams
|
||||
use the scorecard.
|
||||
- Reference findings are converted into concrete adapter, observability, or
|
||||
operator-surface deltas.
|
||||
@@ -1183,14 +1043,6 @@ Acceptance:
|
||||
ECC-Tools commit `632e059` adds sanitized target-account readback, so the
|
||||
final operator gate should verify the exact Marketplace test account without
|
||||
printing its login or raw KV key names.
|
||||
ECC-Tools PR #79 redacts the billing announcement gate account output;
|
||||
PR #80 requires failure reasons in runtime receipts; PRs #81/#82 preserve
|
||||
and render AgentShield fleet approval IDs; PR #83 makes Linear follow-up
|
||||
sync idempotent by external ID; PR #84 syncs hosted AgentShield
|
||||
remediation items into Linear; PR #85 emits hosted job observability events
|
||||
including budget-blocked outcomes; PRs #86/#87 read those events back into
|
||||
hosted status comments and hosted depth-plan check-runs; and PR #88 exposes
|
||||
authenticated hosted observability API readback for operator dashboards.
|
||||
2. Run `npm run billing:announcement-gate -- --preflight --account
|
||||
<github-login>`, then run the same command without `--preflight` against a
|
||||
Marketplace-managed test account and require `announcementGate.ready ===
|
||||
|
||||
@@ -9,7 +9,7 @@ status update can claim a lane is current.
|
||||
| Surface | Role | Current rule |
|
||||
| --- | --- | --- |
|
||||
| GitHub PRs/issues/discussions | Public queue and review state | Recheck live counts before every significant merge batch and before release approval. |
|
||||
| Linear project | Executive roadmap and stakeholder status update | Use project documents and project/issue comments because project status updates are disabled in this workspace; create/reuse issues for durable execution lanes. |
|
||||
| Linear project | Executive roadmap and stakeholder status update | Post project status updates while issue capacity blocks issue creation. Create/reuse issues only when workspace capacity is available. |
|
||||
| Local handoff | Durable operator continuity | Update the active handoff after every merge batch, queue drain, skipped release gate, or blocked external action. |
|
||||
| Repo roadmap | Auditable planning mirror | Keep `docs/ECC-2.0-GA-ROADMAP.md` aligned to merged PR evidence and unresolved gates. |
|
||||
| `scripts/work-items.js` | Local tracker bridge | Sync GitHub PRs/issues into the SQLite work-items store for status snapshots and blocked follow-up. |
|
||||
@@ -41,12 +41,9 @@ After a significant merge batch, update Linear and the handoff with:
|
||||
4. Deferred or skipped work and the explicit reason.
|
||||
5. The next one or two implementation slices.
|
||||
|
||||
When Linear project status updates are unavailable, use a project document plus
|
||||
project/issue comments instead of creating placeholder issues. Issue capacity is
|
||||
available for durable execution lanes, but do not use that issue capacity as a
|
||||
substitute for evidence-backed project status. Create or reuse exact-title
|
||||
issues only when the lane needs a durable execution owner, and link those issues
|
||||
to repo evidence.
|
||||
When Linear issue capacity is unavailable, use a project status update instead
|
||||
of creating placeholder issues. When issue capacity is available, create or
|
||||
reuse exact-title issues and link them to the repo evidence.
|
||||
|
||||
## Realtime Boundary
|
||||
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
- verify package, plugin, marketplace, OpenCode, and agent metadata stays at `2.0.0-rc.1`
|
||||
- verify `ecc2/Cargo.toml` stays at `0.1.0` for rc.1; `ecc2/` remains an alpha control-plane scaffold
|
||||
- complete `publication-readiness.md` with fresh evidence before any GitHub release, npm publish, plugin submission, or announcement post
|
||||
- run `npm run release:approval-gate -- --format json` after owner approvals
|
||||
and live URL readbacks are recorded; it must return ready true before any
|
||||
publish, upload, social, or outbound action
|
||||
- rerun the release name/plugin publication checklist before creating a
|
||||
GitHub prerelease, publishing npm, pushing Claude plugin tags, recording the
|
||||
Codex marketplace path, or posting public copy
|
||||
|
||||
@@ -71,7 +71,7 @@ Reason:
|
||||
| Codex repo marketplace | Local and GitHub-ref temp-home marketplace add smokes passed on Codex CLI `0.131.0` | `.codex-plugin/plugin.json`, `.agents/plugins/marketplace.json`, repo/personal marketplace evidence | Plugin owner | Official Plugin Directory listing requires OpenAI submission/listing evidence |
|
||||
| Codex official Plugin Directory | OpenAI docs describe the curated official directory; ECC has not submitted or received listing evidence | Directory submission link or OpenAI approval path once available | Plugin owner | Track as an ITO-56/ITO-46 follow-up; do not claim an official listing |
|
||||
| OpenCode package | `npm run build:opencode` passed | Built `.opencode` package metadata inside npm tarball | Package owner | No separate public plugin channel identified; follows npm |
|
||||
| Billing/native payments | Marketplace Pro target readback passed; announcement remains blocked by ITO-61 | 2026-05-20 selected-target readback, webhook provenance, `INTERNAL_API_SECRET`, live announcement gate | ECC Tools owner | Do not include native-payments claim in rc.1 announcement until the live gate passes |
|
||||
| Billing/native payments | Announcement remains blocked by ITO-61 | Marketplace Pro target readback, webhook provenance, `INTERNAL_API_SECRET`, announcement gate | ECC Tools owner | Do not include native-payments claim in rc.1 announcement |
|
||||
| Social/longform copy | Drafts exist | Final live GitHub, npm, Claude, Codex, billing URLs | Release owner | Publish only after release/package/plugin URLs exist |
|
||||
|
||||
## Package Rename After rc.1
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
This dashboard is generated by `npm run operator:dashboard`. It is an operator snapshot, not release approval.
|
||||
|
||||
Generated: 2026-05-20T01:28:52.541Z
|
||||
Commit: a2bbc45504ff55f09e9e06be0e253d72f3c54f90
|
||||
Generated: 2026-05-19T15:08:49.870Z
|
||||
Commit: ac7434ea8f39166b11e9d06ce64b38c4fb8d9202
|
||||
Status: work remaining
|
||||
|
||||
## Current Status
|
||||
@@ -42,7 +42,7 @@ Growth lanes: GitHub Sponsors and OSS partner sponsors; ECC Tools Pro subscripti
|
||||
| Produce the ECC 2.0 release video suite | docs/releases/2.0.0-rc.1/video-suite-production.md and npm run release:video-suite | current | video-suite gate is ready with 15/15 source assets, 13/13 suite artifacts, 12/12 publish candidates, primary self-eval, and zero detected black-frame segments recorded in May 19 evidence | final owner approval, upload, and public video URLs remain approval-gated |
|
||||
| Prepare sponsor, partner, consulting, podcast, talk, and Discussion copy | docs/releases/2.0.0-rc.1/partner-sponsor-talks-pack.md | in_progress | sponsor outbound, platform partner DM, consulting intro, talk/podcast pitch, GitHub Discussion announcement, CTA hooks, and do-not-send gate are drafted | replace final URLs after publication gates, then get explicit approval before outbound or personal-account posts |
|
||||
| Advance AgentShield enterprise iteration | AgentShield PR evidence plus enterprise roadmap | in_progress | AgentShield policy promotion `reviewItems` landed in `87aec47`; package-manager hardening drift detection landed in `28d08c7`; workflow action runtime pins were refreshed in `659f569`; npm age-gate guidance was corrected in `ee585cd`; package-manager hardening Action outputs landed in `1124535`; policy-promotion Action outputs and runtime-smoke job-summary evidence landed in `1593925`; fleet review ticket payloads and current Mini Shai-Hulud IOC breadcrumbs landed in `840952a`; ECC-Tools consumes those outputs in `8658951`, surfaces operator-readable status/pack/count/digest telemetry in `16c537f`, and renders hosted promotion judge audit traces in `05d4e82`; all are mirrored in the GA roadmap | deepen live operator approval/readback after Marketplace/payment gates |
|
||||
| Advance ECC Tools native payments and AI-native harness-agnostic app | ECC Tools PR evidence, billing gate, hosted analysis lanes | in_progress | billing announcement gate, hosted analysis lanes, AgentShield fleet-summary consumption, hosted finding evidence paths, harness-route policy linking, policy-promotion Action-output telemetry, operator-visible promotion output details, hosted promotion judge audit traces, billing announcement preflight, aggregate production billing KV readback, Wrangler OAuth readback, target-account billing readback, provenance-aware Marketplace billing-state gates, sanitized Marketplace plan/action provenance counts, ready Marketplace Pro target selection, hosted team-learning feedback controls, and ECC-Tools Dependabot alert remediation are mirrored in the GA roadmap | obtain or rotate the local/internal INTERNAL_API_SECRET bearer-token path, then run the live billing announcement gate for the selected Marketplace Pro target before publishing native-payments copy |
|
||||
| Advance ECC Tools native payments and AI-native harness-agnostic app | ECC Tools PR evidence, billing gate, hosted analysis lanes | in_progress | billing announcement gate, hosted analysis lanes, AgentShield fleet-summary consumption, hosted finding evidence paths, harness-route policy linking, policy-promotion Action-output telemetry, operator-visible promotion output details, hosted promotion judge audit traces, billing announcement preflight, aggregate production billing KV readback, Wrangler OAuth readback, target-account billing readback, provenance-aware Marketplace billing-state gates, sanitized Marketplace plan/action provenance counts, hosted team-learning feedback controls, and ECC-Tools Dependabot alert remediation are mirrored in the GA roadmap | create or verify Marketplace-managed Pro target billing-state with webhook provenance, configure the target account and INTERNAL_API_SECRET, then rerun target readback and the live announcement gate |
|
||||
| Audit, prune, or attach legacy work | docs/stale-pr-salvage-ledger.md and legacy inventory | current | legacy salvage ledger and inventory are current; all localization tails are attached to Linear ITO-55 for manual language-owner review | repeat legacy scan before release |
|
||||
| Keep Linear roadmap detailed and progress tracking synchronized | Linear project mirror plus progress-sync contract | current | Linear live sync is current with the May 19 post-PR #2002 sync document, project comment, and active issue-lane updates; progress-sync contract defines the file-backed work-items/status path | repeat Linear/project status update and local work-items sync after each significant merge batch |
|
||||
| Provide ECC 2.0 observability for self-use | observability readiness gate | complete | observability:ready command and readiness doc exist | runtime/dashboard implementation can continue after release gates |
|
||||
@@ -54,7 +54,7 @@ Growth lanes: GitHub Sponsors and OSS partner sponsors; ECC Tools Pro subscripti
|
||||
- `release-notes-and-notifications`: final live release/npm/plugin/billing URLs and publish approval still pending
|
||||
- `partner-sponsor-talks-pack`: replace final URLs after publication gates, then get explicit approval before outbound or personal-account posts
|
||||
- `agentshield-enterprise-iteration`: deepen live operator approval/readback after Marketplace/payment gates
|
||||
- `ecc-tools-next-level`: obtain or rotate the local/internal INTERNAL_API_SECRET bearer-token path, then run the live billing announcement gate for the selected Marketplace Pro target before publishing native-payments copy
|
||||
- `ecc-tools-next-level`: create or verify Marketplace-managed Pro target billing-state with webhook provenance, configure the target account and INTERNAL_API_SECRET, then rerun target readback and the live announcement gate
|
||||
|
||||
## Next Work Order
|
||||
|
||||
@@ -63,4 +63,4 @@ Growth lanes: GitHub Sponsors and OSS partner sponsors; ECC Tools Pro subscripti
|
||||
3. Review the owner-approved primary launch video candidates, choose the final cuts, upload after approval, and attach public video URLs to the release pack.
|
||||
4. Replace final release, npm, plugin, billing, and video URLs in the partner/sponsor/talk pack, then get explicit approval before outbound.
|
||||
5. Repeat ITO-57 Linear/project status sync after the next significant merge batch or advisory-source refresh.
|
||||
6. Obtain or rotate the local/internal INTERNAL_API_SECRET bearer-token path, then run the live billing announcement gate for the selected Marketplace Pro target before publishing native-payments copy.
|
||||
6. Create or verify Marketplace-managed Pro target billing-state with webhook provenance, configure the target account and INTERNAL_API_SECRET, then rerun target readback and the live announcement gate before publishing native-payments copy.
|
||||
|
||||
@@ -8,19 +8,18 @@ release action after the final evidence commands are rerun from the intended
|
||||
release commit.
|
||||
|
||||
Source commit for the clean evidence baseline this packet extends:
|
||||
`9819626459a662773be7d0b1c18d82c1316b8c36`.
|
||||
`ac7434ea8f39166b11e9d06ce64b38c4fb8d9202`.
|
||||
|
||||
## Current Evidence
|
||||
|
||||
| Evidence | Current recorded state | Repeat before approval |
|
||||
| --- | --- | --- |
|
||||
| Platform audit | ready true, 0 open PRs, 0 open issues, 0 discussion gaps, 0 dirty files | yes |
|
||||
| Preview pack smoke | ready true, digest `531328aaaa53`, 5/5 checks | yes |
|
||||
| Release approval gate | ready false, digest `ef8f49f727b7`, 4/6 checks pass; owner decisions and live URL readbacks pending | yes |
|
||||
| Preview pack smoke | ready true, digest `790430aef4a8`, 5/5 checks | yes |
|
||||
| Video suite | ready true, 15/15 source assets, 13/13 suite artifacts, 12/12 publish candidates | yes |
|
||||
| Release surface tests | 27/27 passed after this packet was added | yes |
|
||||
| Full local suite | 2568/2568 passed before PR #2013 merged; focused GateGuard regression passed 91/91 again before PR #2011 merged | yes |
|
||||
| GitHub CI | PR #1998, PR #1999, PR #2000, PR #2001, PR #2002, PR #2004, PR #2008, post-PR #2006 `main`, PR #2009, post-PR #2009 `main`, post-PR #2011 `main`, and post-PR #2013 `main` all merged or advanced after green required checks | verify current head |
|
||||
| Full local suite | 2550/2550 passed after this packet was added | yes |
|
||||
| GitHub CI | PR #1998, PR #1999, PR #2000, PR #2001, PR #2002, and PR #2004 merged after green required checks | verify current head |
|
||||
|
||||
## Decision Register
|
||||
|
||||
@@ -57,7 +56,6 @@ Run these from the exact release commit before approving publication:
|
||||
git status --short --branch
|
||||
node scripts/platform-audit.js --json
|
||||
npm run preview-pack:smoke -- --format json
|
||||
npm run release:approval-gate -- --format json
|
||||
npm run release:video-suite -- --format json
|
||||
npm run harness:adapters -- --check
|
||||
npm run harness:audit -- --format json
|
||||
@@ -66,7 +64,6 @@ npm run security:ioc-scan
|
||||
npm audit --audit-level=moderate
|
||||
npm audit signatures
|
||||
node tests/docs/ecc2-release-surface.test.js
|
||||
node tests/hooks/gateguard-fact-force.test.js
|
||||
node tests/run-all.js
|
||||
cd ecc2 && cargo test
|
||||
```
|
||||
|
||||
@@ -17,7 +17,6 @@ surfaces, or posting announcements.
|
||||
| `docs/architecture/observability-readiness.md` | Local operator-readiness gate | Verified by `npm run observability:ready` |
|
||||
| `docs/architecture/progress-sync-contract.md` | GitHub, Linear, handoff, roadmap, and work-item sync boundary | Checked by `node scripts/platform-audit.js --json` |
|
||||
| `scripts/preview-pack-smoke.js` | Deterministic preview-pack smoke gate | Verified by `npm run preview-pack:smoke` |
|
||||
| `scripts/release-approval-gate.js` | Final owner-decision, live-URL, and launch-copy gate | Must return ready true before any release publish, package publish, plugin tag, video upload, announcement, or outbound batch |
|
||||
| `docs/releases/2.0.0-rc.1/release-notes.md` | GitHub release copy source | Must be refreshed with final live release/package/plugin URLs before publication |
|
||||
| `docs/releases/2.0.0-rc.1/quickstart.md` | Clone-to-first-workflow path | Covers clone, install, verify, first skill, and harness switch |
|
||||
| `docs/releases/2.0.0-rc.1/launch-checklist.md` | Operator launch checklist | Must remain approval-gated for release, package, plugin, and announcement actions |
|
||||
@@ -26,7 +25,7 @@ surfaces, or posting announcements.
|
||||
| `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-16.md` | Current May 16/17 queue cleanup, recsys skill merge, GateGuard triage, PR #1947 supply-chain protection, AgentShield #87 plugin-cache confidence evidence, AgentShield #88 evidence-pack inspect/readback, AgentShield #89 evidence-pack fleet routing, AgentShield #90 fleet review items, AgentShield #91 policy export, AgentShield #92 policy promotion, ECC-Tools #76 fleet-summary consumption, ECC-Tools #77 hosted finding evidence paths, ECC-Tools #78 harness policy-route linking, dashboard refresh, and combined Node/Rust/release-surface gate evidence through the May 16 mirror | Must still be repeated from a strict clean checkout before real publication |
|
||||
| `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-17.md` | May 17 queue-zero state, Japanese localization merge, Dependabot TypeScript and Node type merges, post-merge ja-JP lint repair, Mini Shai-Hulud/TanStack protection recheck, npm audit/signature checks, legacy and Linear progress routing, deterministic preview-pack smoke, operator dashboard refresh, Linear sync, and GitHub CI evidence for `27dc2918` | Superseded by the May 18 evidence snapshot; repeat from a strict clean checkout before real publication |
|
||||
| `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-18.md` | May 18 queue-zero state, #1970/#1971/#1972 merge batch, #1978 review/closure, supply-chain recheck, AgentShield evidence mirror, Linear sync, current-head CI/security scan success for `4470e2e6`, and ITO-46 naming/plugin publication closure | Superseded by the May 19 ECC identity, video, and growth evidence snapshot |
|
||||
| `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-19.md` | Current May 19/20 evidence for canonical ECC identity, release video suite, partner/sponsor/talk outreach pack, owner approval packet, release approval gate, May 19 operator dashboard, preview-pack smoke digest `531328aaaa53`, 2568-test local suite, PR #1998 visual QA CI success, PR #1999 dashboard evidence CI success, PR #2000 suite-count evidence success, PR #2001 owner approval packet CI success, PR #2002 owner-approval dashboard gate CI success, PR #2004 Linear readiness evidence sync CI success, PR #2008 supply-chain evidence gate CI success, post-PR #2006 main CI success, PR #2009 project-registry hygiene CI success, post-PR #2009 main CI success, post-PR #2011 GateGuard CI success, post-PR #2013 release-approval-gate CI success, PR #2017 AgentShield adapter evidence sync, ECC-Tools #79 billing-announcement redaction hardening, ECC-Tools #80-#88 runtime-receipt, AgentShield approval-ID, Linear sync, remediation sync, hosted observability event/status/depth-plan/API readback, AgentShield #94 Zed/VS Code adapter coverage, AgentShield #95 Dependabot alert closure, JARVIS #15/#16 queue/deploy repair, and the May 19/20 Linear sync comments | Current strongest readiness snapshot; must still be repeated from a strict clean checkout before real publication |
|
||||
| `docs/releases/2.0.0-rc.1/publication-evidence-2026-05-19.md` | Current May 19 evidence for canonical ECC identity, release video suite, partner/sponsor/talk outreach pack, owner approval packet, May 19 operator dashboard, preview-pack smoke digest `790430aef4a8`, 2550-test local suite, PR #1998 visual QA CI success, PR #1999 dashboard evidence CI success, PR #2000 suite-count evidence success, PR #2001 owner approval packet CI success, PR #2002 owner-approval dashboard gate CI success, PR #2004 Linear readiness evidence sync CI success, and the May 19 Linear sync document | Current strongest readiness snapshot; must still be repeated from a strict clean checkout before real publication |
|
||||
| `docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-17.md` | Previous prompt-to-artifact operator dashboard | Superseded by the May 18 generated dashboard |
|
||||
| `docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-18.md` | Previous prompt-to-artifact operator dashboard | Superseded by the May 19 generated dashboard |
|
||||
| `docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-19.md` | Current prompt-to-artifact operator dashboard | Shows PR/issue/discussion/platform/supply-chain gates current and adds the current `$1,728/mo` to `$10,000/mo` hypergrowth, video owner-approval, and outbound-pack operating lanes |
|
||||
@@ -81,7 +80,6 @@ Run these from the exact release commit before publication:
|
||||
git status --short --branch
|
||||
node scripts/platform-audit.js --json
|
||||
npm run preview-pack:smoke
|
||||
npm run release:approval-gate -- --format json
|
||||
npm run release:video-suite -- --format json
|
||||
npm run harness:adapters -- --check
|
||||
npm run harness:audit -- --format json
|
||||
@@ -100,8 +98,6 @@ The preview pack is assembled, but publication is still blocked until these live
|
||||
surfaces exist and are recorded in a final evidence file:
|
||||
|
||||
- final release URL ledger regenerated from the intended release commit;
|
||||
- `npm run release:approval-gate -- --format json` returning ready true after
|
||||
owner approvals and live URL readbacks are recorded;
|
||||
- final release name/plugin publication checklist rerun from the intended
|
||||
release commit;
|
||||
- GitHub prerelease `v2.0.0-rc.1`;
|
||||
@@ -110,9 +106,8 @@ surfaces exist and are recorded in a final evidence file:
|
||||
- Codex repo-marketplace distribution evidence plus official Plugin Directory
|
||||
availability status;
|
||||
- final announcement URLs in X, LinkedIn, GitHub release, and longform copy;
|
||||
- ECC Tools billing/product readiness evidence, the local/internal announcement
|
||||
bearer-token path, and a live announcement gate pass before any
|
||||
native-payments announcement copy is published.
|
||||
- ECC Tools billing/product readiness evidence before any native-payments
|
||||
announcement copy is published.
|
||||
|
||||
## Result
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ social announcement.
|
||||
|
||||
| Field | Evidence |
|
||||
| --- | --- |
|
||||
| Upstream main | `906e06406e95742944ccb05065f95a7e4dd4a036` |
|
||||
| Upstream main | `ac7434ea8f39166b11e9d06ce64b38c4fb8d9202` |
|
||||
| Git remote | `https://github.com/affaan-m/ECC.git` |
|
||||
| Evidence scope | Current `main` after PR #1990 harness-audit GitHub integration scoring, PR #1991 canonical ECC identity gate, PR #1992 release video-suite gate, PR #1993 growth outreach pack, PR #1994 May 19 publication evidence refresh, PR #1995 operator dashboard refresh, PR #1996 primary render self-eval gate, PR #1997 publish-candidate gate, PR #1998 visual QA gate, PR #1999 video dashboard evidence refresh, PR #2000 suite-count evidence refresh, PR #2001 owner approval packet addition, PR #2002 owner approval dashboard gate refresh, PR #2004 Linear readiness evidence sync, PR #2005 post-PR #2004 evidence refresh, PR #2008 release supply-chain evidence gate fix, PR #2006 per-project Claude Code adapter, PR #2009 continuous-learning project registry hygiene fix, PR #2011 GateGuard quoted git introspection fix, PR #2013 deterministic release approval gate, PR #2017 AgentShield adapter evidence sync, ECC-Tools #80-#88 hosted observability/readback batch, AgentShield #94 Zed/VS Code adapter coverage, and AgentShield #95 Dependabot alert closure |
|
||||
| Evidence scope | Current `main` after PR #1990 harness-audit GitHub integration scoring, PR #1991 canonical ECC identity gate, PR #1992 release video-suite gate, PR #1993 growth outreach pack, PR #1994 May 19 publication evidence refresh, PR #1995 operator dashboard refresh, PR #1996 primary render self-eval gate, PR #1997 publish-candidate gate, PR #1998 visual QA gate, PR #1999 video dashboard evidence refresh, PR #2000 suite-count evidence refresh, PR #2001 owner approval packet addition, PR #2002 owner approval dashboard gate refresh, and PR #2004 Linear readiness evidence sync |
|
||||
| Local status caveat | `git status --short --branch` was clean after pulling `origin/main`; generated evidence files are committed after the source snapshot they describe |
|
||||
|
||||
The release operator must repeat all publish-facing checks from the exact final
|
||||
@@ -23,7 +23,7 @@ release commit with a strictly clean checkout before publishing.
|
||||
| Platform audit | `node scripts/platform-audit.js --json` | Ready true; tracked repos report 0 open PRs, 0 open issues, 0 discussion maintainer-touch gaps, 0 answerable Q&A gaps, 0 conflicting PRs, and 0 blocking dirty files |
|
||||
| Trunk PRs | `gh pr list --repo affaan-m/ECC --state open --json number,title,url,author --limit 100` | `[]` |
|
||||
| Trunk issues | `gh issue list --repo affaan-m/ECC --state open --json number,title,url,author --limit 100` | `[]` |
|
||||
| Discussion audit through platform audit | `node scripts/platform-audit.js --json` | `affaan-m/ECC` discussions enabled; 60 sampled after #2015 setup-location Q&A was answered and accepted; 0 needing maintainer touch; 0 answerable without accepted answer |
|
||||
| Discussion audit through platform audit | `node scripts/platform-audit.js --json` | `affaan-m/ECC` discussions enabled; 59 sampled after #2003 AURA integration proposal; 0 needing maintainer touch; 0 answerable without accepted answer |
|
||||
| Worktree | `git status --short --branch` | `## main...origin/main` |
|
||||
|
||||
Tracked repositories in the platform audit were:
|
||||
@@ -52,67 +52,22 @@ Tracked repositories in the platform audit were:
|
||||
| PR #2001 | Merged the final human decision sheet for release, package, plugin, video, billing, social, and outbound approvals; GitHub Actions run `26102500291` completed successfully |
|
||||
| PR #2002 | Merged the owner-approval dashboard refresh so the operator dashboard fails closed when the final decision sheet is missing or incomplete; CI passed before merge |
|
||||
| PR #2004 | Merged the May 19 Linear readiness evidence sync after PR #2002, including roadmap, dashboard, preview-pack manifest, publication evidence, operator dashboard generator, and release-surface test updates |
|
||||
| PR #2005 | Merged the post-PR #2004 evidence refresh, keeping the May 19 readiness ledger, dashboard, roadmap, and release-surface references current on `main` |
|
||||
| PR #2008 | Merged the release supply-chain evidence gate fix so platform-audit readiness keeps matching current publication evidence |
|
||||
| PR #2006 | Merged the `claude-project` install target for per-project Claude Code adapter support, then fixed the manifest schema enum on top of the feature branch before merge |
|
||||
| PR #2009 | Merged the continuous-learning project registry hygiene fix: non-git hook payloads stay global, no-remote linked worktrees migrate to the main worktree project ID, and `instinct-cli.py projects delete`, `merge`, and `gc` provide operator maintenance commands |
|
||||
| PR #2011 | Merged the GateGuard read-only git introspection tokenizer fix so quoted `git show` pathspecs with spaces are preserved while quoted shell separators stay outside the bypass |
|
||||
| PR #2013 | Merged the deterministic `release:approval-gate` so final publication, package, plugin, video, billing, social, and outbound actions remain blocked until owner decisions and live URL readbacks are complete |
|
||||
| PR #2017 | Merged the AgentShield #94 evidence mirror as `906e06406e95742944ccb05065f95a7e4dd4a036`, syncing roadmap, publication evidence, preview-pack manifest, and supply-chain incident-response surfaces after full GitHub CI passed |
|
||||
|
||||
## Post-Queue-Zero Sync - 2026-05-19 Late Pass
|
||||
|
||||
| Surface | Evidence |
|
||||
| --- | --- |
|
||||
| ECC approval gate | PR #2013 merged as `9819626459a662773be7d0b1c18d82c1316b8c36`; GitHub Actions run `26128749863` completed successfully; `npm run release:approval-gate -- --format json` remains intentionally blocked with digest `ef8f49f727b7`, 4/6 passing, and failures only on owner decisions plus live URL readbacks |
|
||||
| ECC platform audit | `node scripts/platform-audit.js --json` at `2026-05-19T22:45:15Z` returned ready true, 0 open PRs, 0 open issues, 0 discussion maintainer-touch gaps, 0 answerable Q&A gaps, and 0 dirty blockers across `affaan-m/ECC`, `affaan-m/agentshield`, `affaan-m/JARVIS`, `ECC-Tools/ECC-Tools`, and `ECC-Tools/ECC-website` |
|
||||
| ECC-Tools billing hardening | ECC-Tools PR #79 merged as `67ee247ae1b7b50ecc1261ed5d62d65cc8390da8`; preflight and live billing-announcement output now redact account login values to a stable fingerprint while preserving readiness blockers/actions; local validation passed targeted tests, full test suite 678/678, lint, typecheck, manual preflight, and `git diff --check`; post-merge main CI run `26129253509` completed successfully |
|
||||
| JARVIS queue drain | JARVIS PR #15 merged the Dependabot `idna` 3.11 to 3.15 security bump as `4b3685d6ee23b4da1f1a7d22281c6b5d6c0a42c7`; PR checks and post-merge CI/CodeQL passed |
|
||||
| JARVIS deploy repair | JARVIS PR #16 merged as `4369c34babd21d539c420866da51c7a8365f1c9e`; the deploy workflow no longer uses an invalid job-level `secrets.*` condition, Vercel deploy skips cleanly when secrets are absent, backend image build/push succeeds, and main CI, CodeQL, and Deploy runs `26129539376`, `26129539427`, and `26129539425` completed successfully |
|
||||
| Linear roadmap sync | Linear document `ecc-may-19-late-queue-zero-and-release-gate-sync-1c26f65e6b3f`, project comment `d42bf0e2-7a8e-4934-9f3f-e281498ee805`, and issue comments on ITO-44, ITO-50, ITO-54, ITO-56, and ITO-61 record the late-pass queue-zero, release-gate, billing-safety, and progress-sync state. |
|
||||
|
||||
## May 20 Hosted Observability And AgentShield Adapter Sync
|
||||
|
||||
| Surface | Evidence |
|
||||
| --- | --- |
|
||||
| ECC discussion queue | Discussion #2015 was answered and marked accepted with conservative setup guidance: do not install in `C:\`; use a normal workspace; install `ecc@ecc` once through the Claude plugin marketplace; copy only needed rule folders when using manual rules; do not stack plugin plus full manual install. |
|
||||
| ECC platform audit | `node scripts/platform-audit.js --json` at `2026-05-20T00:25:38Z` returned ready true with 0 open PRs, 0 open issues, 0 discussion maintainer-touch gaps, 0 answerable Q&A gaps, 0 conflicting PRs, and 0 dirty blockers across `affaan-m/ECC`, `affaan-m/agentshield`, `affaan-m/JARVIS`, `ECC-Tools/ECC-Tools`, and `ECC-Tools/ECC-website`. |
|
||||
| ECC platform audit recheck | `npm run platform:audit -- --json` at `2026-05-20T00:42:11Z` returned ready true with 0 open PRs, 0 open issues, 0 discussion maintainer-touch gaps, 0 answerable Q&A gaps, 0 conflicting PRs, 0 GitHub errors, and 0 dirty blockers across the same tracked repo set after AgentShield #94 merged. |
|
||||
| ECC-Tools #80/#81/#82 | PR #80 merged runtime-receipt failure-reason enforcement as `4efc8cc858022f84c844690f3298633b081c4398`; PR #81 preserved AgentShield fleet approval IDs as `1fbf635f492284f75ba7166c029c39eb8cc15794`; PR #82 rendered those approval IDs in hosted security review comments/check-runs as `7a7b4d096a176ae80b3a2076c09d45601e36013a`. |
|
||||
| ECC-Tools #83/#84 | PR #83 merged deterministic Linear external-ID reuse for deferred follow-ups as `b6b107f33961bef18a85fb619f3a976eb5d752dd`; PR #84 merged hosted AgentShield remediation sync to Linear as `73bac7058071c55cb30c6b8ac6db779b3660c02c`. Local validation covered focused route/client tests, typecheck, lint, full ECC-Tools test suite, and whitespace checks before merge; GitHub Verify, Security Audit, and Workers Builds passed. |
|
||||
| ECC-Tools #85/#86/#87 | PR #85 merged hosted job observability events as `1637e0f2bfa0a889387f2c20675680ccc5528123`; PR #86 merged hosted status observability readback as `5a9e94d3ff860307c3e7fd9fd065f0de2bd633dd`; PR #87 merged hosted depth-plan observability readback as `508fbc02b63cf1fcb5af2f3624608fa66e53b5d4`. Local validation for the final depth-plan readback slice passed the focused hosted depth-plan route test, full route suite (89/89), typecheck, lint, full ECC-Tools Vitest suite (683/683), and `git diff --check`; GitHub Verify, Security Audit, and Workers Builds passed before merge. |
|
||||
| ECC-Tools #88 | PR #88 merged authenticated hosted observability API readback as `c836ac3fb24ed7e2ae38cd61e41c9651ac9c00f8`. `GET /api/analysis/observability` now summarizes hosted events by event type and job for operator/dashboard readback, skips malformed stale KV records, and the deployment runbook includes the production smoke command. Local verification passed typecheck, lint, full ECC-Tools Vitest suite (686/686), and `git diff --check`; GitHub Verify, Security Audit, and Workers Builds passed before merge. |
|
||||
| AgentShield #94 | PR #94 merged Zed/VS Code adapter coverage as `4caee27acfadb50a4cd024e738b5c3cbd4b0bb03`. AgentShield now reports Zed and VS Code as first-class harness adapters, discovers `.zed/settings.json`, `.zed/tasks.json`, and `.zed` hook-code files, and flags `.zed/setup.mjs` in the AI-tool persistence IOC rule alongside `.vscode/setup.mjs`. Local verification passed typecheck, lint, focused scanner/rule tests, full `npm test` (1822 tests), `npm run build`, and `git diff --check`; GitHub checks passed across GitGuardian, scan suite, self-scan, self-scan examples, Node 18/20/22 CI, CodeRabbit, and Cubic after rerunning a transient artifact-upload failure. |
|
||||
| AgentShield #95 | PR #95 merged the `brace-expansion` Dependabot fix as `25d91f0002214c408da4ceaac7def20bad40ca10`. The lockfile now resolves vulnerable transitive `brace-expansion` 5.x entries to `5.0.6`, local `npm audit --audit-level=moderate` returns 0 vulnerabilities, and `gh api repos/affaan-m/agentshield/dependabot/alerts?state=open` returns `[]`. Local validation passed typecheck, lint, full `npm test` (1822 tests), build, audit, and whitespace checks; GitHub checks passed across Verify Node 18/20/22, self-scan, self-scan examples, Test GitHub Action, GitGuardian, CodeRabbit, and Cubic. |
|
||||
| Linear roadmap sync | Linear ITO-54 comment `74dcc101-3be5-4173-be13-62b80d54f569` and ECC Platform Roadmap project comment `348ea8f5-2a2d-46d9-a0fe-ed99653e7fe5` record the May 20 hosted observability status/depth-plan readback batch; Linear comments `291e2a4b-06e3-4672-a057-cdb141478161` and `b2d35de0-ca49-44cb-982a-ddec229e7691` add the #88 observability API readback; Linear ITO-49 comment `faed69dd-35f5-469d-acb5-ddde6a70d6a1` and project comment `70187c1e-d481-4181-b418-09bd65d54b5e` add the #94 AgentShield Zed/VS Code adapter evidence; Linear ITO-49 comment `371fc3e4-611f-4d20-a23f-67db1260b418`, ITO-57 comment `bd06e252-15c1-4256-b667-caa3f64f5968`, and project comment `22c2c388-2fd1-4dea-a939-6141f40c9a21` add the #95 AgentShield Dependabot alert closure; earlier comments on ITO-54, ITO-48, and the project record the #84 hosted remediation sync and #85 hosted observability event emission batches. |
|
||||
|
||||
## Release And Growth Evidence
|
||||
|
||||
| Gate | Command | Result |
|
||||
| --- | --- | --- |
|
||||
| Release-surface tests | `node tests/docs/ecc2-release-surface.test.js` | 28 passed, 0 failed |
|
||||
| Preview-pack smoke | `npm run preview-pack:smoke -- --format json` | Ready true; digest `531328aaaa53`; 32 required artifacts; 5 passed, 0 failed |
|
||||
| Release approval gate | `npm run release:approval-gate -- --format json` | Expected blocked; digest `ef8f49f727b7`; 4 passed, 2 failed; owner decisions and live URL readbacks remain approval-gated |
|
||||
| Operator dashboard | `npm run operator:dashboard -- --write docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-19.md` | Regenerated from the May 19 `main` baseline with platform audit ready true, 0 tracked PRs, 0 tracked issues, 0 discussion gaps, `$1,728/mo` current MRR, `$10,000/mo` target MRR, the release video suite marked current, and top actions for plugin publication, notifications, outbound approval, AgentShield, and ECC Tools billing |
|
||||
| Supply-chain verification | `npm audit --audit-level=moderate`; `npm audit signatures`; `yarn install --immutable --mode=skip-build` | Current supply-chain refresh found 0 npm vulnerabilities, verified 254 registry signatures and 30 attestations, and accepted the Yarn lock after pinning `@types/node@25.7.0` plus refreshing `brace-expansion` to `5.0.6` / `1.1.14` |
|
||||
| Release-surface tests | `node tests/docs/ecc2-release-surface.test.js` | 27 passed, 0 failed |
|
||||
| Preview-pack smoke | `npm run preview-pack:smoke -- --format json` | Ready true; digest `790430aef4a8`; 31 required artifacts; 5 passed, 0 failed |
|
||||
| Operator dashboard | `npm run operator:dashboard -- --write docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-19.md` | Regenerated from `ac7434ea8f39166b11e9d06ce64b38c4fb8d9202` with platform audit ready true, 0 tracked PRs, 0 tracked issues, 0 discussion gaps, `$1,728/mo` current MRR, `$10,000/mo` target MRR, the release video suite marked current, and top actions for plugin publication, notifications, outbound approval, AgentShield, and ECC Tools billing |
|
||||
| Release video suite | `npm run release:video-suite -- --format json --summary` with `ECC_VIDEO_SOURCE_ROOT` and `ECC_VIDEO_RELEASE_SUITE_ROOT` | Ready true; 15/15 source assets present; 13/13 render, timeline, caption, EDL, and segment artifacts present; 12/12 publish-candidate outputs present with zero detected black-frame segments; primary rough render self-eval passed at 144.759 seconds, 1920x1080, 1 audio stream, and 106.78 MB |
|
||||
| Focused post-merge regression set | `node tests/hooks/detect-project-worktree.test.js`; `node tests/hooks/observe-subdirectory-detection.test.js`; `node tests/scripts/instinct-cli-projects.test.js`; `node tests/hooks/hooks.test.js` | 10/10, 6/6, 5/5, and 237/237 passed after PR #2009 merged |
|
||||
| GateGuard PR #2011 regression | `node tests/hooks/gateguard-fact-force.test.js`; `npm test`; `git diff --check main...HEAD` | 91/91 passed on the PR branch; full local suite passed 2560/2560 before merge; whitespace check passed; focused GateGuard suite passed again on current `main` |
|
||||
| Release approval gate PR #2013 validation | `npm test`; `npm run lint`; `git diff --check`; `npm run preview-pack:smoke -- --format json`; `npm run release:approval-gate -- --format json` | 2568/2568 tests passed before merge; lint and whitespace passed; preview pack stayed ready with digest `531328aaaa53`; release approval gate returned the expected blocked exit with digest `ef8f49f727b7` |
|
||||
| Full local suite | `node tests/run-all.js` | 2568 passed, 0 failed before PR #2013 merge |
|
||||
| Full local suite | `node tests/run-all.js` | 2550 passed, 0 failed |
|
||||
| PR #1998 CI | GitHub Actions run `26099020341` | Completed successfully for `d500de1e9f11c0446b6a1349bd98b522d31f9125`; all reported checks passed, including lint, validation, security scan, coverage, GitGuardian, CodeRabbit, Cubic, and the macOS/Ubuntu/Windows test matrix |
|
||||
| PR #1999 CI | GitHub Actions run `26100148726` | Completed successfully for `90584b6d5e5814bc2ad9a4cd651bebd043de989d`; lint, validation, security scan, coverage, GitGuardian, CodeRabbit, and the macOS/Ubuntu/Windows test matrix passed; Cubic completed neutral and did not block merge |
|
||||
| PR #2001 CI | GitHub Actions run `26102500291` | Completed successfully for `8148340ad14eb32c971346f0cb4cb9431ec0f5de`; required checks passed before merge |
|
||||
| PR #2002 CI | GitHub Actions run `26103853507` | Completed successfully before merge; required checks passed, Cubic remained non-blocking, and PR #2002 merged into `main` as `c7d662c3c68719e5ef0b5305ca3f6782b3214224` |
|
||||
| PR #2004 CI | GitHub Actions run `26105012698` | Completed successfully after rerunning the single failed Windows Node 18 yarn job; required checks passed, Cubic remained non-blocking, and PR #2004 merged into `main` as `ac7434ea8f39166b11e9d06ce64b38c4fb8d9202` |
|
||||
| PR #2005 CI | GitHub Actions run `26106321921` | Completed successfully with 37 completed jobs, 0 failed jobs, and PR #2005 merged into `main` as `d6022d6b8dc5ef1393cf18ae40ee58f646f3754e` |
|
||||
| PR #2008 CI | GitHub Actions run `26108473648` | Completed successfully across the required matrix before merge; non-blocking Cubic skipped after review |
|
||||
| Post-PR #2006 main CI | GitHub Actions run `26109953093` | Completed successfully with 37 completed jobs, 0 failed jobs, and `main` advanced to `98bd517451f38fa0150a53aab4234c2239a47b7e` |
|
||||
| PR #2009 CI | GitHub Actions run `26111313938` | Completed successfully with 37 completed jobs, 0 failed jobs after replacing the brittle fake-worktree regression fixture with a real `git worktree add` setup |
|
||||
| Post-PR #2009 main CI | GitHub Actions run `26111946778` | Completed successfully with 37 completed jobs, 0 failed jobs, and `main` advanced to `bc519e5b8ed42f26c0a5a611756e04351c323f21` |
|
||||
| Post-PR #2011 main CI | GitHub Actions run `26113695068` | Completed successfully with 37 completed jobs, 0 failed jobs, and `main` advanced to `14d88e517b0c56a80c1a6392b1cde2474948d29f` |
|
||||
| Post-PR #2013 main CI | GitHub Actions run `26128749863` | Completed successfully with `main` advanced to `9819626459a662773be7d0b1c18d82c1316b8c36` |
|
||||
| Linear sync | Linear document `ecc-may-19-post-pr-2002-sync-64cef8f668e0` plus project comment `a6411e3a-8c8e-4a58-adba-687e77d4c543`; late-pass document `ecc-may-19-late-queue-zero-and-release-gate-sync-1c26f65e6b3f` plus project comment `d42bf0e2-7a8e-4934-9f3f-e281498ee805` | Project and issue lanes record PR #2002 evidence, discussion #2003 routing, owner-approval dashboard gate, and In Progress status for ITO-47, ITO-48, ITO-49, ITO-51, ITO-54, and ITO-56; the late-pass sync attaches PR #2013, ECC-Tools #79, and JARVIS #15/#16 evidence to ITO-44, ITO-50, ITO-54, ITO-56, and ITO-61 |
|
||||
| Linear sync | Linear document `ecc-may-19-post-pr-2002-sync-64cef8f668e0` plus project comment `a6411e3a-8c8e-4a58-adba-687e77d4c543` | Project and issue lanes now record PR #2002 evidence, discussion #2003 routing, owner-approval dashboard gate, and In Progress status for ITO-47, ITO-48, ITO-49, ITO-51, ITO-54, and ITO-56 |
|
||||
| Public-path sanitization | `node scripts/ci/validate-no-personal-paths.js` through local suite and CI | Passed |
|
||||
| Markdown and whitespace | `markdownlint` focused release docs plus `git diff --check` before PR #1999 | Passed |
|
||||
|
||||
@@ -127,7 +82,7 @@ Tracked repositories in the platform audit were:
|
||||
| Owner approval proof | `owner-approval-packet-2026-05-19.md` centralizes release, package, plugin, video, billing, social, and outbound decision gates |
|
||||
| Business baseline | Hypergrowth command center and partner pack use `$1,728/mo` current MRR, `$10,000/mo` target MRR, and `$8,272/mo` gap |
|
||||
| Operator dashboard | `operator-readiness-dashboard-2026-05-19.md` pulls the growth baseline into the same queue, publication, video, outbound, AgentShield, ECC Tools, Linear, and supply-chain control surface |
|
||||
| Linear progress proof | Linear project document `ecc-may-19-post-pr-2002-sync-64cef8f668e0` mirrors the post-PR #2002 state and records active lanes for launch materials, AgentShield, ECC Tools deep analysis, observability, and final release publication; Linear document `ecc-may-19-late-queue-zero-and-release-gate-sync-1c26f65e6b3f` adds the PR #2013 approval gate, ECC-Tools #79 redaction hardening, and JARVIS #15/#16 queue/deploy repair evidence; May 20 Linear comments `74dcc101-3be5-4173-be13-62b80d54f569`, `348ea8f5-2a2d-46d9-a0fe-ed99653e7fe5`, `291e2a4b-06e3-4672-a057-cdb141478161`, `b2d35de0-ca49-44cb-982a-ddec229e7691`, `faed69dd-35f5-469d-acb5-ddde6a70d6a1`, `70187c1e-d481-4181-b418-09bd65d54b5e`, `371fc3e4-611f-4d20-a23f-67db1260b418`, `bd06e252-15c1-4256-b667-caa3f64f5968`, and `22c2c388-2fd1-4dea-a939-6141f40c9a21` add ECC-Tools #86/#87/#88 hosted observability readback evidence, AgentShield #94 adapter evidence, and AgentShield #95 Dependabot alert closure to ITO-54, ITO-49, ITO-57, and the project |
|
||||
| Linear progress proof | Linear project document `ecc-may-19-post-pr-2002-sync-64cef8f668e0` mirrors the post-PR #2002 state and records active lanes for launch materials, AgentShield, ECC Tools deep analysis, observability, and final release publication |
|
||||
|
||||
## Current Publication Blockers
|
||||
|
||||
@@ -138,12 +93,9 @@ Tracked repositories in the platform audit were:
|
||||
- Codex repo-marketplace distribution is verified by prior evidence, but
|
||||
official Plugin Directory publishing remains blocked on OpenAI submission or
|
||||
listing evidence.
|
||||
- ECC Tools billing/native-payments copy remains blocked until the
|
||||
local/internal `INTERNAL_API_SECRET` bearer-token path is available and the
|
||||
billing announcement gate passes for the ready Marketplace Pro target.
|
||||
ECC-Tools PR #89 (`512bca6`) added `billing:kv-readback --
|
||||
--select-ready-target --require-ready`; its 2026-05-20 production run cleared
|
||||
the old missing-target-state blocker without printing the account login.
|
||||
- ECC Tools billing/native-payments copy remains blocked until a Marketplace
|
||||
Pro purchase/webhook path writes ready production billing state for a target
|
||||
Marketplace test account and the billing announcement gate passes.
|
||||
- Release notes, X, LinkedIn, GitHub release, GitHub Discussion, longform copy,
|
||||
sponsor outreach, partner outreach, consulting copy, conference pitches, and
|
||||
podcast pitches still need final live URLs plus human approval before posting
|
||||
@@ -154,17 +106,10 @@ Tracked repositories in the platform audit were:
|
||||
## Result
|
||||
|
||||
The tracked public PR queue, issue queue, discussion queue, canonical ECC
|
||||
identity, release video suite, preview pack, growth outreach packet, per-project
|
||||
Claude Code adapter surface, continuous-learning project registry hygiene,
|
||||
GateGuard quoted git introspection fix, deterministic release approval gate,
|
||||
ECC-Tools billing-announcement redaction hardening, ECC-Tools hosted
|
||||
observability readback, AgentShield Zed/VS Code adapter coverage, AgentShield
|
||||
Dependabot alert closure, and JARVIS
|
||||
security/deploy queue repairs are current on May 20, 2026 for ECC `main`
|
||||
through `906e06406e95742944ccb05065f95a7e4dd4a036`, ECC-Tools `main`
|
||||
through `c836ac3fb24ed7e2ae38cd61e41c9651ac9c00f8`, and AgentShield `main`
|
||||
through `25d91f0002214c408da4ceaac7def20bad40ca10`. The remaining video work is owner
|
||||
approval, upload, and public URL attachment, not render or QA production.
|
||||
identity, release video suite, preview pack, and growth outreach packet are
|
||||
current on May 19, 2026 for `main` through
|
||||
`ac7434ea8f39166b11e9d06ce64b38c4fb8d9202`. The remaining video work is
|
||||
owner approval, upload, and public URL attachment, not render or QA production.
|
||||
|
||||
This improves publication readiness but does not replace the approval-gated
|
||||
release, package, plugin, billing, Discord, and announcement steps in
|
||||
|
||||
@@ -101,23 +101,22 @@ Record the exact commit SHA and command output before any publication action:
|
||||
|
||||
| Evidence | Command | Required result | Recorded output |
|
||||
| --- | --- | --- | --- |
|
||||
| Clean release branch | `git status --short --branch` | On intended release commit; no unrelated files | Current May 19 baseline `bc519e5b8ed42f26c0a5a611756e04351c323f21`: `## main...origin/main`; repeat from the exact final publication commit before release |
|
||||
| Preview-pack smoke | `npm run preview-pack:smoke` | Preview pack artifacts, Hermes boundary, final verification command list, and publication blockers pass | `publication-evidence-2026-05-19.md`: ready yes, digest `531328aaaa53`, 32 artifacts, 5 passed, 0 failed; repeat in the final strict clean-checkout release pass |
|
||||
| Release approval gate | `npm run release:approval-gate -- --format json` | Ready true only after owner decision rows are approved, live release/package/plugin/video/billing URLs are recorded, and launch/outbound copy has no placeholders or private paths | Current May 19 state is intentionally blocked because owner decisions and live URL readbacks remain approval-gated |
|
||||
| Harness audit | `npm run harness:audit -- --format json` | 80/80 passing | Current release gate: 80/80 across 8 applicable categories, 0 top actions |
|
||||
| Adapter scorecard | `npm run harness:adapters -- --check` | PASS | Current release gate: PASS, 11 adapters |
|
||||
| Observability readiness | `npm run observability:ready` | 21/21 passing | Current release gate: 21/21, ready true |
|
||||
| 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 | Current release gate keeps Release Safety passing at 3/3; repeat the JSON gate from the exact final release commit |
|
||||
| Supply-chain verification | `npm audit --audit-level=moderate`; `npm audit signatures`; `yarn install --immutable --mode=skip-build`; `cd ecc2 && cargo audit -q`; Dependabot alerts; GitGuardian Security Checks | 0 vulnerabilities/alerts, registry signatures verified, package-manager locks accepted, GitGuardian clean | Current supply-chain branch: `npm audit` found 0 vulnerabilities; `npm audit signatures` verified 254 registry signatures and 30 attestations; Yarn immutable install accepted the lock after pinning `@types/node@25.7.0` and moving `brace-expansion` to `5.0.6` / `1.1.14`; PR #2008 CI `26108473648`, post-PR #2006 main CI `26109953093`, PR #2009 CI `26111313938`, and post-PR #2009 main CI `26111946778` completed with 0 failures |
|
||||
| Root suite | `node tests/run-all.js` | 0 failures | Current May 19 local suite: 2568 passed, 0 failed before PR #2013 merged; post-PR #2009 focused regressions also passed for worktree detection, observe subdirectory/global fallback, project maintenance CLI, and the hooks suite |
|
||||
| Markdown lint | `npx markdownlint-cli '**/*.md' --ignore node_modules` | 0 failures | Current release gate: focused lint passed for `publication-readiness.md`, `publication-evidence-2026-05-19.md`, and `docs/ECC-2.0-GA-ROADMAP.md` |
|
||||
| Package surface | `node tests/scripts/npm-publish-surface.test.js` | 0 failures; no Python bytecode in npm tarball | Current release gate: 2/2 passed |
|
||||
| Release surface | `node tests/docs/ecc2-release-surface.test.js` | 0 failures | Current release gate: 27/27 passed after refreshing the discussion-count assertion to the post-PR #2005 baseline |
|
||||
| Clean release branch | `git status --short --branch` | On intended release commit; no unrelated files | `3304848b`: `## main...origin/main`; repeat from the exact final publication commit before release |
|
||||
| Preview-pack smoke | `npm run preview-pack:smoke` | Preview pack artifacts, Hermes boundary, final verification command list, and publication blockers pass | `publication-evidence-2026-05-19.md`: ready yes, digest `790430aef4a8`, 31 artifacts, 5 passed, 0 failed; repeat in the final strict clean-checkout release pass |
|
||||
| Harness audit | `npm run harness:audit -- --format json` | 70/70 passing | `99e01ded`: 70/70, 0 top actions |
|
||||
| Adapter scorecard | `npm run harness:adapters -- --check` | PASS | `99e01ded`: PASS, 11 adapters |
|
||||
| Observability readiness | `npm run observability:ready` | 21/21 passing | `publication-evidence-2026-05-18.md`: 21/21, ready yes |
|
||||
| Release safety gate | `npm run observability:ready -- --format json` | Release Safety category passing with publication readiness, supply-chain, workflow security, package surface, and release-surface evidence | May 18 evidence keeps release safety passing; repeat the JSON gate from the exact final release commit |
|
||||
| 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-19.md` plus CI `26093792219`: GitGuardian and security scan passed; prior May 18 npm registry signatures and IOC scans remain the latest detailed supply-chain evidence |
|
||||
| Root suite | `node tests/run-all.js` | 0 failures | Current dashboard branch: local `node tests/run-all.js` passed 2550/2550; PR #2001 CI `26102500291` passed the previous full OS/runtime/package-manager matrix |
|
||||
| Markdown lint | `npx markdownlint-cli '**/*.md' --ignore node_modules` | 0 failures | CI `26093792219`: markdownlint passed on the growth-pack PR; rerun after any release-copy edits |
|
||||
| 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 | May 19 evidence refresh: 27/27 passed after adding the video suite, partner/sponsor/talk gates, owner approval packet, and roadmap evidence mirror |
|
||||
| Optional Rust surface | `cd ecc2 && cargo test` | 0 failures or explicit deferral | `publication-evidence-2026-05-16.md`: 462/462 passed, existing warnings only |
|
||||
| Queue baseline | `node scripts/platform-audit.js --json` across trunk, AgentShield, JARVIS, ECC Tools, and ECC website | Under 20 open PRs and under 20 open issues | Current May 19 baseline after PR #2009: platform audit ready true, 0 open PRs, 0 open issues, 0 conflicting PRs, and 0 blocking dirty files across tracked repos |
|
||||
| Discussion baseline | `node scripts/platform-audit.js --json` and `node scripts/discussion-audit.js --json` | No unmanaged active discussion queue and no answerable Q&A missing an accepted answer | Post-PR #2005 baseline: platform audit sampled 59 trunk discussions, 0 needing maintainer touch, 0 answerable discussions missing accepted answer; `docs/architecture/discussion-response-playbook.md` records response templates and security escalation rules |
|
||||
| Queue baseline | `node scripts/platform-audit.js --json` across trunk, AgentShield, JARVIS, ECC Tools, and ECC website | Under 20 open PRs and under 20 open issues | `3304848b`: platform audit ready, 0 open PRs, 0 open issues, 0 conflicting PRs, and 0 blocking dirty files |
|
||||
| Discussion baseline | `node scripts/platform-audit.js --json` and `node scripts/discussion-audit.js --json` | No unmanaged active discussion queue and no answerable Q&A missing an accepted answer | `3304848b`: platform audit sampled 58 trunk discussions, 0 needing maintainer touch, 0 answerable discussions missing accepted answer; `docs/architecture/discussion-response-playbook.md` records response templates and security escalation rules |
|
||||
| Linear roadmap | Linear project and issue readback | Detailed roadmap exists with release, security, AgentShield, ECC Tools, legacy, and observability lanes | May 18 Linear comments include ITO-57 `3fe5b2b7-c4fe-401c-a317-b40d72119cb3` and ITO-44 `fb4a4f33-6c2d-421a-bbdb-63cfad3e3ee4`; earlier evidence records the project and 16 issue lanes |
|
||||
| Operator readiness dashboard | `npm run operator:dashboard -- --json` | Current queue state mapped to macro-goal deliverables and incomplete gaps | Current May 19 dashboard is refreshed from the post-PR #2009 baseline; platform audit ready true, 0 open PRs, 0 open issues, 0 discussion gaps, 0 dirty files, release video suite current, and publication gates still approval-gated |
|
||||
| Operator readiness dashboard | `npm run operator:dashboard -- --json` | Current queue state mapped to macro-goal deliverables and incomplete gaps | `3304848b`: regenerated May 19 dashboard from current main; platform audit ready true, 0 open PRs, 0 open issues, 0 discussion gaps, 0 dirty files, release video suite current, and publication gates still approval-gated |
|
||||
| Release URL ledger | `docs/releases/2.0.0-rc.1/release-url-ledger-2026-05-19.md` plus placeholder-marker scan | Live links and approval-gated links are separated before announcement copy is posted | Ledger records public repo/docs/npm/OpenAI Codex documentation URLs and blocks GitHub release/npm/plugin/billing/social URLs until approval-gated checks pass |
|
||||
| Release name and plugin publication checklist | `docs/releases/2.0.0-rc.1/release-name-plugin-publication-checklist-2026-05-18.md` | Name/package/plugin values are frozen, final-release commands are listed, and Claude/Codex publication paths cite current official docs | Checklist keeps `ECC`, `ecc-universal`, and plugin slug `ecc` for rc.1; no npm rename, npm publish, plugin tag, official listing, billing claim, or announcement before final evidence |
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ npm pack --dry-run --json
|
||||
npm publish --tag next --dry-run
|
||||
npm run build:opencode
|
||||
npm run preview-pack:smoke
|
||||
npm run release:approval-gate -- --format json
|
||||
```
|
||||
|
||||
If a command is unavailable on the release machine, record the exact error and
|
||||
|
||||
@@ -47,7 +47,6 @@ npm view ecc-universal name version dist-tags --json
|
||||
codex plugin marketplace add --help
|
||||
rg -n "TODO|TBD|PLACEHOLDER" docs/releases/2.0.0-rc.1
|
||||
npm run preview-pack:smoke
|
||||
npm run release:approval-gate -- --format json
|
||||
```
|
||||
|
||||
Do not post the social or notification copy until the approval-gated URLs above
|
||||
|
||||
@@ -133,15 +133,13 @@ Use the same production shape as Video Use while keeping the ECC-specific media
|
||||
stack intact:
|
||||
|
||||
1. Treat transcript and timeline data as the editing surface.
|
||||
2. Keep visual inspection on demand: filmstrips, waveform/timeline composites,
|
||||
or frame samples only at ambiguous cut points.
|
||||
3. Propose the edit strategy and EDL before rendering.
|
||||
2. Inspect filmstrip or frame samples only at ambiguous cut points.
|
||||
3. Keep an edit decision list before rendering.
|
||||
4. Cut deterministically with FFmpeg.
|
||||
5. Add proof overlays with Remotion or Manim where product claims need visual
|
||||
evidence.
|
||||
6. Export the MP4 plus editable timeline and caption state.
|
||||
7. Run cut-boundary, audio, caption, black-frame, and product-claim self-eval
|
||||
before any upload or social post.
|
||||
7. Run self-eval before any upload or social post.
|
||||
|
||||
Do not dump frames into the repo. Frame samples used for self-eval belong in the
|
||||
local release suite workspace.
|
||||
|
||||
@@ -42,30 +42,6 @@ MRR growth should come from four lanes at once:
|
||||
- consulting and implementation contracts;
|
||||
- talks, podcasts, conference demos, and partner webinars that create inbound.
|
||||
|
||||
## Second Hypergrowth Phase
|
||||
|
||||
The release should behave like a proof engine, not a name-change announcement.
|
||||
Every public surface should make the product obvious in the first screen,
|
||||
clip, paragraph, or demo:
|
||||
|
||||
| Workstream | Public proof | Revenue path |
|
||||
| --- | --- | --- |
|
||||
| Product category | ECC as the harness-native operator system, not a Claude-only config pack | Converts confused OSS traffic into install, Pro, and sponsor intent |
|
||||
| Harness coverage | Claude Code, Codex, OpenCode, Cursor, Gemini, Zed, GitHub Copilot, and terminal workflows shown as execution surfaces | Partner conversations with tools, IDEs, model providers, and platform teams |
|
||||
| Control plane | `ecc2/` alpha dashboard/status/session surface and Hermes operator shell clearly framed as directionally live | Consulting and team implementation sprints |
|
||||
| Enterprise trust | AgentShield, supply-chain, release, observability, and CI gates shown as repeatable evidence | Security vendors, code-review vendors, platform sponsors, and enterprise pilots |
|
||||
| Media engine | Primary launch video, five proof clips, browser captures, transcripts, EDLs, captions, and editable timelines | Social reach, podcast/talk booking, sponsor proof, partner demos |
|
||||
| Community funnel | GitHub Discussions, Discord once approved, sponsor tiers, Pro, and consulting CTAs routed without clutter | Repeatable inbound, not one-off launch spikes |
|
||||
|
||||
The operating rhythm after launch should be weekly:
|
||||
|
||||
1. one product proof clip;
|
||||
2. one security or release-discipline proof clip;
|
||||
3. one partner/sponsor/talk outreach batch after owner approval;
|
||||
4. one public discussion or community prompt;
|
||||
5. one measurable funnel readback covering repo traffic, sponsor clicks, Pro
|
||||
conversions, MRR movement, and inbound replies.
|
||||
|
||||
## Release Gates
|
||||
|
||||
| Lane | Done when | Current action |
|
||||
|
||||
@@ -26,9 +26,8 @@ credentials:
|
||||
with historical malicious `node-ipc` versions also blocked by ECC because
|
||||
they carried destructive or unauthorized file-writing behavior.
|
||||
- The live IOC set includes persistence through Claude Code
|
||||
`.claude/settings.json`, VS Code `.vscode/tasks.json`, Zed
|
||||
`.zed/tasks.json`, and OS-level `gh-token-monitor` LaunchAgent/systemd
|
||||
services. Some variants add
|
||||
`.claude/settings.json`, VS Code `.vscode/tasks.json`, and OS-level
|
||||
`gh-token-monitor` LaunchAgent/systemd services. Some variants add
|
||||
`~/.config/gh-token-monitor/token` plus a dead-man-switch token description
|
||||
`IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner`, malicious workflow
|
||||
files such as `.github/workflows/codeql_analysis.yml`, and Python runtime
|
||||
@@ -180,7 +179,7 @@ Escalate to a maintainer security review before any release or merge if:
|
||||
|
||||
- a dependency lockfile references a package named in an active advisory;
|
||||
- `node scripts/ci/scan-supply-chain-iocs.js --home` finds Claude Code,
|
||||
VS Code, Zed, or OS-level persistence indicators;
|
||||
VS Code, or OS-level persistence indicators;
|
||||
- a workflow combines `pull_request_target` with dependency installation,
|
||||
cache restore/save, PR-head checkout, or write permissions;
|
||||
- a release workflow combines `id-token: write` with shared cache usage;
|
||||
|
||||
40
package-lock.json
generated
40
package-lock.json
generated
@@ -21,7 +21,7 @@
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@opencode-ai/plugin": "^1.0.0",
|
||||
"@types/node": "25.7.0",
|
||||
"@types/node": "^25.8.0",
|
||||
"c8": "^11.0.0",
|
||||
"eslint": "^9.39.2",
|
||||
"globals": "^17.4.0",
|
||||
@@ -398,13 +398,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz",
|
||||
"integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==",
|
||||
"version": "25.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz",
|
||||
"integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.21.0"
|
||||
"undici-types": ">=7.24.0 <7.24.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
@@ -497,9 +497,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
|
||||
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1210,9 +1210,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1680,9 +1680,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/markdownlint-cli/node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2666,9 +2666,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/test-exclude/node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2745,9 +2745,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz",
|
||||
"integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==",
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
||||
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
"scripts/operator-readiness-dashboard.js",
|
||||
"scripts/platform-audit.js",
|
||||
"scripts/preview-pack-smoke.js",
|
||||
"scripts/release-approval-gate.js",
|
||||
"scripts/release-video-suite.js",
|
||||
"scripts/hooks/",
|
||||
"scripts/install-apply.js",
|
||||
@@ -313,7 +312,6 @@
|
||||
"observability:ready": "node scripts/observability-readiness.js",
|
||||
"operator:dashboard": "node scripts/operator-readiness-dashboard.js",
|
||||
"preview-pack:smoke": "node scripts/preview-pack-smoke.js",
|
||||
"release:approval-gate": "node scripts/release-approval-gate.js",
|
||||
"release:video-suite": "node scripts/release-video-suite.js",
|
||||
"platform:audit": "node scripts/platform-audit.js",
|
||||
"discussion:audit": "node scripts/discussion-audit.js",
|
||||
@@ -337,7 +335,7 @@
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@opencode-ai/plugin": "^1.0.0",
|
||||
"@types/node": "25.7.0",
|
||||
"@types/node": "^25.8.0",
|
||||
"c8": "^11.0.0",
|
||||
"eslint": "^9.39.2",
|
||||
"globals": "^17.4.0",
|
||||
|
||||
@@ -119,65 +119,6 @@ function tokenize(segment) {
|
||||
return segment.split(/\s+/).filter(Boolean);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tokenize a short allowlisted shell command while preserving quoted
|
||||
* arguments. This is intentionally smaller than a full shell parser: the
|
||||
* caller rejects shell control characters before invoking it, so this only
|
||||
* needs to keep spaces inside quotes together for read-only git commands.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string[] | null}
|
||||
*/
|
||||
function tokenizeAllowlistedShellWords(input) {
|
||||
const tokens = [];
|
||||
let current = '';
|
||||
let quote = null;
|
||||
let escaped = false;
|
||||
|
||||
for (const char of String(input || '')) {
|
||||
if (escaped) {
|
||||
current += char;
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (quote) {
|
||||
if (char === quote) {
|
||||
quote = null;
|
||||
} else {
|
||||
current += char;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '"' || char === "'") {
|
||||
quote = char;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/\s/.test(char)) {
|
||||
if (current) {
|
||||
tokens.push(current);
|
||||
current = '';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
current += char;
|
||||
}
|
||||
|
||||
if (escaped) current += '\\';
|
||||
if (quote) return null;
|
||||
if (current) tokens.push(current);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip a leading path and trailing `.exe` from a command token so
|
||||
* `/usr/bin/git`, `git.exe`, and `GIT` all normalize to `git`.
|
||||
@@ -651,16 +592,8 @@ function isReadOnlyGitIntrospection(command) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const segments = splitCommandSegments(trimmed);
|
||||
if (segments.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const tokens = tokenizeAllowlistedShellWords(trimmed);
|
||||
if (!tokens) {
|
||||
return false;
|
||||
}
|
||||
if (commandBasename(tokens[0]) !== 'git' || tokens.length < 2) {
|
||||
const tokens = trimmed.split(/\s+/);
|
||||
if (tokens[0] !== 'git' || tokens.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -680,7 +613,7 @@ function isReadOnlyGitIntrospection(command) {
|
||||
}
|
||||
|
||||
if (subcommand === 'show') {
|
||||
return args.length === 1 && !args[0].startsWith('--') && /^[a-zA-Z0-9._:/ -]+$/.test(args[0]);
|
||||
return args.length === 1 && !args[0].startsWith('--') && /^[a-zA-Z0-9._:/-]+$/.test(args[0]);
|
||||
}
|
||||
|
||||
if (subcommand === 'branch') {
|
||||
|
||||
@@ -342,11 +342,6 @@ function agentShieldEnterpriseEvidence(roadmap) {
|
||||
}
|
||||
|
||||
function eccToolsNextLevelEvidence(roadmap) {
|
||||
if (roadmap.includes('select-ready-target')
|
||||
|| roadmap.includes('f14ed2fe-a219-470c-8119-63429e197027')) {
|
||||
return 'billing announcement gate, hosted analysis lanes, AgentShield fleet-summary consumption, hosted finding evidence paths, harness-route policy linking, policy-promotion Action-output telemetry, operator-visible promotion output details, hosted promotion judge audit traces, billing announcement preflight, aggregate production billing KV readback, Wrangler OAuth readback, target-account billing readback, provenance-aware Marketplace billing-state gates, sanitized Marketplace plan/action provenance counts, ready Marketplace Pro target selection, hosted team-learning feedback controls, and ECC-Tools Dependabot alert remediation are mirrored in the GA roadmap';
|
||||
}
|
||||
|
||||
if (roadmap.includes('69ca535')
|
||||
|| roadmap.includes('team feedback controls')
|
||||
|| roadmap.includes('e56fc1a')) {
|
||||
@@ -392,12 +387,6 @@ function eccToolsNextLevelEvidence(roadmap) {
|
||||
}
|
||||
|
||||
function eccToolsNextLevelGap(roadmap) {
|
||||
if (roadmap.includes('select-ready-target')
|
||||
|| roadmap.includes('f14ed2fe-a219-470c-8119-63429e197027')
|
||||
|| roadmap.includes('old "no Marketplace-managed Pro target billing-state" blocker is cleared')) {
|
||||
return 'obtain or rotate the local/internal INTERNAL_API_SECRET bearer-token path, then run the live billing announcement gate for the selected Marketplace Pro target before publishing native-payments copy';
|
||||
}
|
||||
|
||||
if (roadmap.includes('1Password CLI authorization timed out')
|
||||
|| roadmap.includes('Cloudflare API auth returned `Authentication error [code: 10000]`')) {
|
||||
return 'authorize Cloudflare API or 1Password CLI access, configure the target Marketplace Pro account and INTERNAL_API_SECRET, create or replay Marketplace Pro webhook state, then rerun target readback and the live announcement gate';
|
||||
@@ -1000,7 +989,7 @@ function buildReport(options) {
|
||||
releaseVideoWorkOrder,
|
||||
'Replace final release, npm, plugin, billing, and video URLs in the partner/sponsor/talk pack, then get explicit approval before outbound.',
|
||||
'Repeat ITO-57 Linear/project status sync after the next significant merge batch or advisory-source refresh.',
|
||||
'Obtain or rotate the local/internal INTERNAL_API_SECRET bearer-token path, then run the live billing announcement gate for the selected Marketplace Pro target before publishing native-payments copy.',
|
||||
'Create or verify Marketplace-managed Pro target billing-state with webhook provenance, configure the target account and INTERNAL_API_SECRET, then rerun target readback and the live announcement gate before publishing native-payments copy.',
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -472,7 +472,7 @@ function buildLocalEvidenceChecks(rootDir) {
|
||||
),
|
||||
buildCheck(
|
||||
'release-evidence-current',
|
||||
includesAll(evidence, ['Release video suite', 'growth outreach', 'Operator dashboard', 'GitGuardian', 'macOS/Ubuntu/Windows test matrix', '2568 passed']) ? 'pass' : 'fail',
|
||||
includesAll(evidence, ['Release video suite', 'growth outreach', 'Operator dashboard', 'GitGuardian', 'macOS/Ubuntu/Windows test matrix', '2550 passed']) ? 'pass' : 'fail',
|
||||
'rc.1 evidence includes current release, video, growth, and CI artifacts',
|
||||
{ path: 'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-19.md' }
|
||||
),
|
||||
|
||||
@@ -18,7 +18,6 @@ const REQUIRED_ARTIFACTS = [
|
||||
'docs/architecture/observability-readiness.md',
|
||||
'docs/architecture/progress-sync-contract.md',
|
||||
'scripts/preview-pack-smoke.js',
|
||||
'scripts/release-approval-gate.js',
|
||||
`${RELEASE_DIR}/release-notes.md`,
|
||||
`${RELEASE_DIR}/quickstart.md`,
|
||||
`${RELEASE_DIR}/launch-checklist.md`,
|
||||
@@ -48,7 +47,6 @@ const REQUIRED_VERIFICATION_COMMANDS = [
|
||||
'git status --short --branch',
|
||||
'node scripts/platform-audit.js --json',
|
||||
'npm run preview-pack:smoke',
|
||||
'npm run release:approval-gate -- --format json',
|
||||
'npm run release:video-suite -- --format json',
|
||||
'npm run harness:adapters -- --check',
|
||||
'npm run harness:audit -- --format json',
|
||||
|
||||
@@ -1,553 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const RELEASE = '2.0.0-rc.1';
|
||||
const RELEASE_DIR = `docs/releases/${RELEASE}`;
|
||||
const SCHEMA_VERSION = 'ecc.release-approval-gate.v1';
|
||||
const SCRIPT_PATH = 'scripts/release-approval-gate.js';
|
||||
const OWNER_PACKET_PATH = `${RELEASE_DIR}/owner-approval-packet-2026-05-19.md`;
|
||||
const URL_LEDGER_PATH = `${RELEASE_DIR}/release-url-ledger-2026-05-19.md`;
|
||||
const PREVIEW_MANIFEST_PATH = `${RELEASE_DIR}/preview-pack-manifest.md`;
|
||||
const REQUIRED_COMMAND = 'npm run release:approval-gate -- --format json';
|
||||
|
||||
const REQUIRED_DECISIONS = [
|
||||
{
|
||||
id: 'github-prerelease',
|
||||
label: 'GitHub prerelease',
|
||||
},
|
||||
{
|
||||
id: 'npm-next-publish',
|
||||
label: 'npm `next` publish',
|
||||
},
|
||||
{
|
||||
id: 'claude-plugin-tag',
|
||||
label: 'Claude plugin tag',
|
||||
},
|
||||
{
|
||||
id: 'codex-repo-marketplace',
|
||||
label: 'Codex repo marketplace',
|
||||
},
|
||||
{
|
||||
id: 'ecc-tools-billing-language',
|
||||
label: 'ECC Tools billing language',
|
||||
},
|
||||
{
|
||||
id: 'video-upload',
|
||||
label: 'Video upload',
|
||||
},
|
||||
{
|
||||
id: 'social-and-longform',
|
||||
label: 'X, LinkedIn, GitHub Discussion, longform',
|
||||
},
|
||||
{
|
||||
id: 'outbound-growth',
|
||||
label: 'Sponsor, partner, consulting, conference, podcast outreach',
|
||||
},
|
||||
];
|
||||
|
||||
const REQUIRED_URL_SURFACES = [
|
||||
{
|
||||
id: 'github-prerelease-url',
|
||||
label: 'GitHub prerelease URL',
|
||||
exampleUrl: 'https://github.com/affaan-m/ECC/releases/tag/v2.0.0-rc.1',
|
||||
},
|
||||
{
|
||||
id: 'npm-rc-package-url',
|
||||
label: 'npm rc package URL',
|
||||
exampleUrl: 'https://www.npmjs.com/package/ecc-universal/v/2.0.0-rc.1',
|
||||
},
|
||||
{
|
||||
id: 'claude-plugin-tag-url',
|
||||
label: 'Claude plugin tag URL',
|
||||
exampleUrl: 'https://github.com/affaan-m/ECC/releases/tag/ecc--v2.0.0-rc.1',
|
||||
},
|
||||
{
|
||||
id: 'codex-repo-marketplace-evidence',
|
||||
label: 'Codex repo-marketplace evidence',
|
||||
exampleUrl: 'https://github.com/affaan-m/ECC/tree/v2.0.0-rc.1/.codex-plugin',
|
||||
},
|
||||
{
|
||||
id: 'primary-launch-video-url',
|
||||
label: 'Primary launch video URL',
|
||||
exampleUrl: 'https://x.com/affaanmustafa/status/0000000000000000000',
|
||||
},
|
||||
{
|
||||
id: 'short-clip-urls',
|
||||
label: 'Short clip URLs',
|
||||
exampleUrl: 'https://x.com/affaanmustafa/status/0000000000000000001',
|
||||
},
|
||||
{
|
||||
id: 'ecc-tools-billing-readiness-url',
|
||||
label: 'ECC Tools billing/readiness URL',
|
||||
exampleUrl: 'https://github.com/ECC-Tools',
|
||||
},
|
||||
];
|
||||
|
||||
const ANNOUNCEMENT_FILES = [
|
||||
`${RELEASE_DIR}/release-notes.md`,
|
||||
`${RELEASE_DIR}/x-thread.md`,
|
||||
`${RELEASE_DIR}/linkedin-post.md`,
|
||||
`${RELEASE_DIR}/article-outline.md`,
|
||||
`${RELEASE_DIR}/partner-sponsor-talks-pack.md`,
|
||||
'docs/business/social-launch-copy.md',
|
||||
];
|
||||
|
||||
function usage() {
|
||||
console.log([
|
||||
'Usage: node scripts/release-approval-gate.js [--format <text|json>] [--root <dir>]',
|
||||
'',
|
||||
'Final approval gate for ECC 2.0 rc.1 publication and outbound actions.',
|
||||
'',
|
||||
'Options:',
|
||||
' --format <text|json> Output format (default: text)',
|
||||
' --json Alias for --format json',
|
||||
' --root <dir> Repository root to inspect (default: cwd)',
|
||||
' --help, -h Show this help',
|
||||
].join('\n'));
|
||||
}
|
||||
|
||||
function readArgValue(args, index, flagName) {
|
||||
const value = args[index + 1];
|
||||
if (!value || value.startsWith('--')) {
|
||||
throw new Error(`${flagName} requires a value`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = argv.slice(2);
|
||||
const parsed = {
|
||||
format: 'text',
|
||||
help: false,
|
||||
root: path.resolve(process.cwd()),
|
||||
};
|
||||
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
|
||||
if (arg === '--help' || arg === '-h') {
|
||||
parsed.help = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--json') {
|
||||
parsed.format = 'json';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--format') {
|
||||
parsed.format = readArgValue(args, index, arg).toLowerCase();
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith('--format=')) {
|
||||
parsed.format = arg.slice('--format='.length).toLowerCase();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--root') {
|
||||
parsed.root = path.resolve(readArgValue(args, index, arg));
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith('--root=')) {
|
||||
parsed.root = path.resolve(arg.slice('--root='.length));
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
if (!['text', 'json'].includes(parsed.format)) {
|
||||
throw new Error(`Invalid format: ${parsed.format}. Use text or json.`);
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function readText(rootDir, relativePath) {
|
||||
try {
|
||||
return fs.readFileSync(path.join(rootDir, relativePath), 'utf8');
|
||||
} catch (_error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function fileExists(rootDir, relativePath) {
|
||||
return fs.existsSync(path.join(rootDir, relativePath));
|
||||
}
|
||||
|
||||
function safeParseJson(text) {
|
||||
if (!text.trim()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (_error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeLabel(value) {
|
||||
return String(value)
|
||||
.replace(/[`*_]/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function normalizeState(value) {
|
||||
return String(value)
|
||||
.replace(/[`*_]/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function splitMarkdownRow(row) {
|
||||
const trimmed = row.trim();
|
||||
if (!trimmed.startsWith('|') || !trimmed.endsWith('|')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return trimmed
|
||||
.slice(1, -1)
|
||||
.split('|')
|
||||
.map(cell => cell.trim());
|
||||
}
|
||||
|
||||
function parseDecisionRegister(packet) {
|
||||
const decisions = new Map();
|
||||
|
||||
for (const line of packet.split('\n')) {
|
||||
const cells = splitMarkdownRow(line);
|
||||
if (cells.length < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const [decision, state] = cells;
|
||||
const normalizedDecision = normalizeLabel(decision);
|
||||
if (
|
||||
!normalizedDecision
|
||||
|| normalizedDecision === 'decision'
|
||||
|| /^-+$/.test(normalizedDecision)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
decisions.set(normalizedDecision, normalizeState(state));
|
||||
}
|
||||
|
||||
return decisions;
|
||||
}
|
||||
|
||||
function isApproved(state) {
|
||||
return state === 'approve' || state === 'approved';
|
||||
}
|
||||
|
||||
function lineNumberForIndex(text, index) {
|
||||
return text.slice(0, index).split('\n').length;
|
||||
}
|
||||
|
||||
function findAnnouncementOffenders(rootDir, relativePaths) {
|
||||
const offenders = [];
|
||||
const privatePathPattern = /\/Users\/(?!\.\.\.)[A-Za-z0-9._-]+|\/home\/(?!user|runner)[A-Za-z0-9._-]+/g;
|
||||
const anglePlaceholderPattern = /<(?!(?:https?:\/\/|mailto:|#))[^>\n]*(?:url|link|todo|tbd|placeholder)[^>\n]*>/gi;
|
||||
const barePlaceholderPattern = /\bTODO\b|\bTBD\b|\bPLACEHOLDER\b/g;
|
||||
|
||||
for (const relativePath of relativePaths) {
|
||||
const text = readText(rootDir, relativePath);
|
||||
if (!text) {
|
||||
offenders.push({
|
||||
path: relativePath,
|
||||
line: 1,
|
||||
marker: 'missing file',
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const match of text.matchAll(privatePathPattern)) {
|
||||
offenders.push({
|
||||
path: relativePath,
|
||||
line: lineNumberForIndex(text, match.index),
|
||||
marker: match[0],
|
||||
});
|
||||
}
|
||||
|
||||
for (const match of text.matchAll(anglePlaceholderPattern)) {
|
||||
offenders.push({
|
||||
path: relativePath,
|
||||
line: lineNumberForIndex(text, match.index),
|
||||
marker: match[0],
|
||||
});
|
||||
}
|
||||
|
||||
for (const match of text.matchAll(barePlaceholderPattern)) {
|
||||
offenders.push({
|
||||
path: relativePath,
|
||||
line: lineNumberForIndex(text, match.index),
|
||||
marker: match[0],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return offenders;
|
||||
}
|
||||
|
||||
function ledgerBlockers(ledger) {
|
||||
const blockers = [];
|
||||
|
||||
if (/^##\s+Approval-Gated URLs\s*$/im.test(ledger)) {
|
||||
blockers.push('approval-gated URL section still present');
|
||||
}
|
||||
|
||||
for (const [pattern, label] of [
|
||||
[/not published yet/i, 'not-published marker still present'],
|
||||
[/must return/i, 'must-return readback marker still present'],
|
||||
[/Gate before use/i, 'gate-before-use column still present'],
|
||||
[/\bpending\b/i, 'pending marker still present'],
|
||||
[/\bblocked\b/i, 'blocked marker still present'],
|
||||
]) {
|
||||
if (pattern.test(ledger)) {
|
||||
blockers.push(label);
|
||||
}
|
||||
}
|
||||
|
||||
return blockers;
|
||||
}
|
||||
|
||||
function makeCheck(id, status, evidence, fix) {
|
||||
return {
|
||||
id,
|
||||
status,
|
||||
evidence,
|
||||
fix: status === 'pass' ? '' : fix,
|
||||
};
|
||||
}
|
||||
|
||||
function topActionsForChecks(checks) {
|
||||
const actions = [];
|
||||
const failedIds = new Set(checks.filter(check => check.status !== 'pass').map(check => check.id));
|
||||
|
||||
if (failedIds.has('release-approval-script-registered')) {
|
||||
actions.push('Wire release:approval-gate into package.json, package files, and the preview-pack manifest.');
|
||||
}
|
||||
|
||||
if (failedIds.has('owner-decisions-approved')) {
|
||||
actions.push('Approve, defer, or block each owner decision row explicitly after final evidence is rerun from the release commit.');
|
||||
}
|
||||
|
||||
if (failedIds.has('release-url-ledger-finalized')) {
|
||||
actions.push('Replace approval-gated URL ledger rows with live readback URLs from the approved release, package, plugin, video, and billing surfaces.');
|
||||
}
|
||||
|
||||
if (failedIds.has('final-evidence-command-listed')) {
|
||||
actions.push('Add release:approval-gate to the final evidence command lists before asking for publication approval.');
|
||||
}
|
||||
|
||||
if (failedIds.has('announcement-copy-finalized')) {
|
||||
actions.push('Remove unresolved placeholders and private local paths from launch, social, and outbound copy.');
|
||||
}
|
||||
|
||||
if (failedIds.has('public-action-guard-present')) {
|
||||
actions.push('Restore the explicit no-outbound/no-publish authorization boundary in the owner packet.');
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
function buildReport(options = {}) {
|
||||
const rootDir = path.resolve(options.root || process.cwd());
|
||||
const packageJson = safeParseJson(readText(rootDir, 'package.json')) || {};
|
||||
const packageScripts = packageJson.scripts || {};
|
||||
const packageFiles = Array.isArray(packageJson.files) ? packageJson.files : [];
|
||||
const ownerPacket = readText(rootDir, OWNER_PACKET_PATH);
|
||||
const ledger = readText(rootDir, URL_LEDGER_PATH);
|
||||
const manifest = readText(rootDir, PREVIEW_MANIFEST_PATH);
|
||||
const decisions = parseDecisionRegister(ownerPacket);
|
||||
|
||||
const missingDecisions = [];
|
||||
const unapprovedDecisions = [];
|
||||
for (const decision of REQUIRED_DECISIONS) {
|
||||
const state = decisions.get(normalizeLabel(decision.label));
|
||||
if (!state) {
|
||||
missingDecisions.push(decision.label);
|
||||
} else if (!isApproved(state)) {
|
||||
unapprovedDecisions.push(`${decision.label}=${state}`);
|
||||
}
|
||||
}
|
||||
|
||||
const missingUrlSurfaces = REQUIRED_URL_SURFACES
|
||||
.filter(surface => !ledger.includes(surface.label))
|
||||
.map(surface => surface.label);
|
||||
const urlBlockers = ledgerBlockers(ledger);
|
||||
const announcementOffenders = findAnnouncementOffenders(rootDir, ANNOUNCEMENT_FILES);
|
||||
const commandListedIn = [
|
||||
ownerPacket.includes(REQUIRED_COMMAND) ? OWNER_PACKET_PATH : '',
|
||||
ledger.includes(REQUIRED_COMMAND) ? URL_LEDGER_PATH : '',
|
||||
manifest.includes(REQUIRED_COMMAND) ? PREVIEW_MANIFEST_PATH : '',
|
||||
].filter(Boolean);
|
||||
|
||||
const checks = [
|
||||
makeCheck(
|
||||
'release-approval-script-registered',
|
||||
packageScripts['release:approval-gate'] === `node ${SCRIPT_PATH}`
|
||||
&& packageFiles.includes(SCRIPT_PATH)
|
||||
&& fileExists(rootDir, SCRIPT_PATH)
|
||||
&& manifest.includes(`\`${SCRIPT_PATH}\``)
|
||||
&& manifest.includes(REQUIRED_COMMAND)
|
||||
? 'pass'
|
||||
: 'fail',
|
||||
'package script, npm package file entry, local script, and preview-pack manifest reference',
|
||||
'Add release:approval-gate to package scripts, package files, and preview-pack-manifest.md.'
|
||||
),
|
||||
makeCheck(
|
||||
'owner-decisions-approved',
|
||||
missingDecisions.length === 0 && unapprovedDecisions.length === 0 ? 'pass' : 'fail',
|
||||
missingDecisions.length === 0 && unapprovedDecisions.length === 0
|
||||
? `${REQUIRED_DECISIONS.length} owner decision rows are approved`
|
||||
: `missing decisions: ${missingDecisions.join(', ') || 'none'}; pending decisions: ${unapprovedDecisions.join(', ') || 'none'}`,
|
||||
'Set every required owner decision row to approve only after the final release evidence has been rerun.'
|
||||
),
|
||||
makeCheck(
|
||||
'release-url-ledger-finalized',
|
||||
ledger
|
||||
&& missingUrlSurfaces.length === 0
|
||||
&& urlBlockers.length === 0
|
||||
? 'pass'
|
||||
: 'fail',
|
||||
ledger && missingUrlSurfaces.length === 0 && urlBlockers.length === 0
|
||||
? `${REQUIRED_URL_SURFACES.length} final URL surfaces are recorded without approval-gated blockers`
|
||||
: `missing URL surfaces: ${missingUrlSurfaces.join(', ') || 'none'}; blockers: ${urlBlockers.join(', ') || 'none'}`,
|
||||
'Regenerate the release URL ledger after the approved publication actions and record live readback URLs.'
|
||||
),
|
||||
makeCheck(
|
||||
'final-evidence-command-listed',
|
||||
commandListedIn.length === 3 ? 'pass' : 'fail',
|
||||
commandListedIn.length === 3
|
||||
? `${REQUIRED_COMMAND} is listed in owner packet, URL ledger, and preview manifest`
|
||||
: `${REQUIRED_COMMAND} listed in: ${commandListedIn.join(', ') || 'none'}`,
|
||||
'List release:approval-gate in every final evidence command block.'
|
||||
),
|
||||
makeCheck(
|
||||
'announcement-copy-finalized',
|
||||
announcementOffenders.length === 0 ? 'pass' : 'fail',
|
||||
announcementOffenders.length === 0
|
||||
? `${ANNOUNCEMENT_FILES.length} launch/outbound copy files have no placeholders or private paths`
|
||||
: `offenders: ${announcementOffenders.map(item => `${item.path}:${item.line}`).join(', ')}`,
|
||||
'Replace placeholders with live URLs and remove private local paths from launch/outbound copy.'
|
||||
),
|
||||
makeCheck(
|
||||
'public-action-guard-present',
|
||||
ownerPacket.includes(
|
||||
'No outbound email, personal-account post, package publish, plugin tag, or billing announcement is authorized by this packet alone.'
|
||||
)
|
||||
? 'pass'
|
||||
: 'fail',
|
||||
'owner packet preserves the explicit no-public-action authorization boundary',
|
||||
'Restore the owner-packet sentence that blocks outbound, posts, package publish, plugin tags, and billing announcements.'
|
||||
),
|
||||
];
|
||||
|
||||
const failed = checks.filter(check => check.status !== 'pass');
|
||||
const digest = crypto
|
||||
.createHash('sha256')
|
||||
.update(JSON.stringify(checks.map(check => [check.id, check.status, check.evidence])))
|
||||
.digest('hex')
|
||||
.slice(0, 12);
|
||||
|
||||
return {
|
||||
schema_version: SCHEMA_VERSION,
|
||||
release: RELEASE,
|
||||
ready: failed.length === 0,
|
||||
digest,
|
||||
summary: {
|
||||
passed: checks.length - failed.length,
|
||||
failed: failed.length,
|
||||
total: checks.length,
|
||||
},
|
||||
top_actions: topActionsForChecks(checks),
|
||||
checks,
|
||||
};
|
||||
}
|
||||
|
||||
function renderText(report) {
|
||||
const lines = [
|
||||
'ECC release approval gate',
|
||||
`Release: ${report.release}`,
|
||||
`Ready: ${report.ready ? 'yes' : 'no'}`,
|
||||
`Digest: ${report.digest}`,
|
||||
'',
|
||||
'Checks:',
|
||||
];
|
||||
|
||||
for (const check of report.checks) {
|
||||
lines.push(`- ${check.status} ${check.id}: ${check.evidence}`);
|
||||
if (check.fix) {
|
||||
lines.push(` fix: ${check.fix}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (report.top_actions.length > 0) {
|
||||
lines.push('');
|
||||
lines.push('Top actions:');
|
||||
for (const action of report.top_actions) {
|
||||
lines.push(`- ${action}`);
|
||||
}
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
lines.push(`Passed: ${report.summary.passed}`);
|
||||
lines.push(`Failed: ${report.summary.failed}`);
|
||||
|
||||
return `${lines.join('\n')}\n`;
|
||||
}
|
||||
|
||||
function main() {
|
||||
let parsed;
|
||||
|
||||
try {
|
||||
parsed = parseArgs(process.argv);
|
||||
} catch (error) {
|
||||
console.error(`Error: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (parsed.help) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
const report = buildReport({ root: parsed.root });
|
||||
|
||||
if (parsed.format === 'json') {
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
} else {
|
||||
process.stdout.write(renderText(report));
|
||||
}
|
||||
|
||||
if (!report.ready) {
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ANNOUNCEMENT_FILES,
|
||||
REQUIRED_COMMAND,
|
||||
REQUIRED_DECISIONS,
|
||||
REQUIRED_URL_SURFACES,
|
||||
buildReport,
|
||||
parseArgs,
|
||||
renderText,
|
||||
};
|
||||
@@ -116,13 +116,7 @@ except(KeyError, TypeError, ValueError):
|
||||
# If cwd was provided in stdin, use it for project detection
|
||||
if [ -n "$STDIN_CWD" ] && [ -d "$STDIN_CWD" ]; then
|
||||
_GIT_ROOT=$(git -C "$STDIN_CWD" rev-parse --show-toplevel 2>/dev/null || true)
|
||||
if [ -n "$_GIT_ROOT" ]; then
|
||||
export CLAUDE_PROJECT_DIR="$_GIT_ROOT"
|
||||
unset CLV2_NO_PROJECT
|
||||
else
|
||||
unset CLAUDE_PROJECT_DIR
|
||||
export CLV2_NO_PROJECT=1
|
||||
fi
|
||||
export CLAUDE_PROJECT_DIR="${_GIT_ROOT:-$STDIN_CWD}"
|
||||
fi
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
@@ -75,42 +75,16 @@ _clv2_normalize_remote_url() {
|
||||
fi
|
||||
}
|
||||
|
||||
_clv2_main_worktree_root() {
|
||||
local root="$1"
|
||||
[ -z "$root" ] && return 0
|
||||
command -v git >/dev/null 2>&1 || return 0
|
||||
|
||||
git -C "$root" worktree list --porcelain 2>/dev/null | while IFS= read -r line; do
|
||||
case "$line" in
|
||||
worktree\ *)
|
||||
printf '%s\n' "${line#worktree }"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
_clv2_detect_project() {
|
||||
local project_root=""
|
||||
local project_name=""
|
||||
local project_id=""
|
||||
local source_hint=""
|
||||
|
||||
if [ "${CLV2_NO_PROJECT:-0}" = "1" ]; then
|
||||
_CLV2_PROJECT_ID="global"
|
||||
_CLV2_PROJECT_NAME="global"
|
||||
_CLV2_PROJECT_ROOT=""
|
||||
_CLV2_PROJECT_DIR="${_CLV2_HOMUNCULUS_DIR}"
|
||||
mkdir -p "$_CLV2_PROJECT_DIR"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 1. Try CLAUDE_PROJECT_DIR env var
|
||||
if [ -n "$CLAUDE_PROJECT_DIR" ] && [ -d "$CLAUDE_PROJECT_DIR" ] && command -v git &>/dev/null; then
|
||||
project_root=$(git -C "$CLAUDE_PROJECT_DIR" rev-parse --show-toplevel 2>/dev/null || true)
|
||||
if [ -n "$project_root" ]; then
|
||||
source_hint="env"
|
||||
fi
|
||||
if [ -n "$CLAUDE_PROJECT_DIR" ] && [ -d "$CLAUDE_PROJECT_DIR" ]; then
|
||||
project_root="$CLAUDE_PROJECT_DIR"
|
||||
source_hint="env"
|
||||
fi
|
||||
|
||||
# 2. Try git repo root from CWD (only if git is available)
|
||||
@@ -127,7 +101,6 @@ _clv2_detect_project() {
|
||||
_CLV2_PROJECT_NAME="global"
|
||||
_CLV2_PROJECT_ROOT=""
|
||||
_CLV2_PROJECT_DIR="${_CLV2_HOMUNCULUS_DIR}"
|
||||
mkdir -p "$_CLV2_PROJECT_DIR"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -160,14 +133,7 @@ _clv2_detect_project() {
|
||||
normalized_remote=$(_clv2_normalize_remote_url "$remote_url")
|
||||
fi
|
||||
|
||||
local fallback_root="$project_root"
|
||||
if [ -z "$remote_url" ]; then
|
||||
local main_worktree_root
|
||||
main_worktree_root=$(_clv2_main_worktree_root "$project_root")
|
||||
[ -n "$main_worktree_root" ] && fallback_root="$main_worktree_root"
|
||||
fi
|
||||
|
||||
local hash_input="${normalized_remote:-${remote_url:-$fallback_root}}"
|
||||
local hash_input="${normalized_remote:-${remote_url:-$project_root}}"
|
||||
# Prefer Python for consistent SHA256 behavior across shells/platforms.
|
||||
# Pass the value via env var and encode as UTF-8 inside Python so the hash
|
||||
# is locale-independent (shells vary between UTF-8 / CP932 / CP1252, which
|
||||
|
||||
@@ -22,7 +22,6 @@ import os
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
import shutil
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timedelta, timezone
|
||||
@@ -195,64 +194,26 @@ def _yaml_quote(value: str) -> str:
|
||||
# Project Detection (Python equivalent of detect-project.sh)
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
def _git_repo_root(cwd: Optional[str] = None) -> Optional[str]:
|
||||
args = ["git"]
|
||||
if cwd:
|
||||
args.extend(["-C", cwd])
|
||||
args.extend(["rev-parse", "--show-toplevel"])
|
||||
try:
|
||||
result = subprocess.run(args, capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
return result.stdout.strip()
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _main_worktree_root(project_root: str) -> str:
|
||||
"""Return the main worktree root when project_root is a linked worktree."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "-C", project_root, "worktree", "list", "--porcelain"],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
return project_root
|
||||
|
||||
if result.returncode != 0:
|
||||
return project_root
|
||||
|
||||
for line in result.stdout.splitlines():
|
||||
if line.startswith("worktree "):
|
||||
main_root = line.split(" ", 1)[1].strip()
|
||||
return main_root or project_root
|
||||
return project_root
|
||||
|
||||
|
||||
def detect_project() -> dict:
|
||||
"""Detect current project context. Returns dict with id, name, root, project_dir."""
|
||||
project_root = None
|
||||
|
||||
if os.environ.get("CLV2_NO_PROJECT") == "1":
|
||||
return {
|
||||
"id": "global",
|
||||
"name": "global",
|
||||
"root": "",
|
||||
"project_dir": HOMUNCULUS_DIR,
|
||||
"instincts_personal": GLOBAL_PERSONAL_DIR,
|
||||
"instincts_inherited": GLOBAL_INHERITED_DIR,
|
||||
"evolved_dir": GLOBAL_EVOLVED_DIR,
|
||||
"observations_file": GLOBAL_OBSERVATIONS_FILE,
|
||||
}
|
||||
|
||||
# 1. CLAUDE_PROJECT_DIR env var
|
||||
env_dir = os.environ.get("CLAUDE_PROJECT_DIR")
|
||||
if env_dir and os.path.isdir(env_dir):
|
||||
project_root = _git_repo_root(env_dir)
|
||||
project_root = env_dir
|
||||
|
||||
# 2. git repo root
|
||||
if not project_root:
|
||||
project_root = _git_repo_root()
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
if result.returncode == 0:
|
||||
project_root = result.stdout.strip()
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
# Normalize: strip trailing slashes to keep basename and hash stable
|
||||
if project_root:
|
||||
@@ -289,10 +250,9 @@ def detect_project() -> dict:
|
||||
if remote_url:
|
||||
remote_url = _strip_remote_credentials(remote_url)
|
||||
|
||||
fallback_root = _main_worktree_root(project_root) if not remote_url else project_root
|
||||
legacy_hash_source = remote_url if remote_url else project_root
|
||||
normalized_remote = _normalize_remote_url(remote_url) if remote_url else ""
|
||||
hash_source = normalized_remote if normalized_remote else (remote_url if remote_url else fallback_root)
|
||||
hash_source = normalized_remote if normalized_remote else legacy_hash_source
|
||||
project_id = _project_hash(hash_source)
|
||||
|
||||
project_dir = PROJECTS_DIR / project_id
|
||||
@@ -392,26 +352,6 @@ def load_registry() -> dict:
|
||||
return {}
|
||||
|
||||
|
||||
def _write_registry(registry: dict) -> None:
|
||||
"""Write the project registry atomically."""
|
||||
REGISTRY_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp_file = REGISTRY_FILE.parent / f".{REGISTRY_FILE.name}.tmp.{os.getpid()}"
|
||||
with open(tmp_file, "w", encoding="utf-8") as f:
|
||||
json.dump(registry, f, indent=2)
|
||||
f.write("\n")
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
os.replace(tmp_file, REGISTRY_FILE)
|
||||
|
||||
|
||||
def _validate_project_id(project_id: str) -> bool:
|
||||
if not project_id or len(project_id) > 128:
|
||||
return False
|
||||
if "/" in project_id or "\\" in project_id or ".." in project_id:
|
||||
return False
|
||||
return bool(re.match(r"^[A-Za-z0-9][A-Za-z0-9._-]*$", project_id))
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# Instinct Parser
|
||||
# ─────────────────────────────────────────────
|
||||
@@ -496,96 +436,6 @@ def _load_instincts_from_dir(directory: Path, source_type: str, scope_label: str
|
||||
return instincts
|
||||
|
||||
|
||||
def _project_counts(project_id: str) -> dict:
|
||||
project_dir = PROJECTS_DIR / project_id
|
||||
personal_dir = project_dir / "instincts" / "personal"
|
||||
inherited_dir = project_dir / "instincts" / "inherited"
|
||||
observations_file = project_dir / "observations.jsonl"
|
||||
|
||||
personal_count = len(_load_instincts_from_dir(personal_dir, "personal", "project"))
|
||||
inherited_count = len(_load_instincts_from_dir(inherited_dir, "inherited", "project"))
|
||||
observations_count = 0
|
||||
if observations_file.exists():
|
||||
try:
|
||||
with open(observations_file, encoding="utf-8") as f:
|
||||
observations_count = sum(1 for _ in f)
|
||||
except OSError:
|
||||
observations_count = 0
|
||||
|
||||
return {
|
||||
"personal": personal_count,
|
||||
"inherited": inherited_count,
|
||||
"observations": observations_count,
|
||||
"total": personal_count + inherited_count + observations_count,
|
||||
}
|
||||
|
||||
|
||||
def _remove_project_storage(project_id: str) -> None:
|
||||
project_dir = PROJECTS_DIR / project_id
|
||||
if project_dir.exists():
|
||||
shutil.rmtree(project_dir)
|
||||
|
||||
|
||||
def _project_instinct_ids(project_dir: Path, source_type: str) -> set[str]:
|
||||
instinct_dir = project_dir / "instincts" / source_type
|
||||
return {
|
||||
inst.get("id")
|
||||
for inst in _load_instincts_from_dir(instinct_dir, source_type, "project")
|
||||
if inst.get("id")
|
||||
}
|
||||
|
||||
|
||||
def _merge_instinct_dir(from_dir: Path, into_dir: Path, existing_ids: set[str]) -> tuple[int, int]:
|
||||
moved = 0
|
||||
skipped = 0
|
||||
if not from_dir.exists():
|
||||
return moved, skipped
|
||||
|
||||
into_dir.mkdir(parents=True, exist_ok=True)
|
||||
for file_path in sorted(from_dir.iterdir()):
|
||||
if not file_path.is_file() or file_path.suffix.lower() not in ALLOWED_INSTINCT_EXTENSIONS:
|
||||
continue
|
||||
try:
|
||||
instincts = parse_instinct_file(file_path.read_text(encoding="utf-8"))
|
||||
except (OSError, UnicodeDecodeError):
|
||||
instincts = []
|
||||
instinct_ids = [inst.get("id") for inst in instincts if inst.get("id")]
|
||||
if any(instinct_id in existing_ids for instinct_id in instinct_ids):
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
target_path = into_dir / file_path.name
|
||||
if target_path.exists():
|
||||
target_path = into_dir / f"{file_path.stem}-{_project_hash(str(file_path))}{file_path.suffix}"
|
||||
shutil.copy2(file_path, target_path)
|
||||
existing_ids.update(instinct_ids)
|
||||
moved += 1
|
||||
|
||||
return moved, skipped
|
||||
|
||||
|
||||
def _append_observations(from_project_dir: Path, into_project_dir: Path) -> int:
|
||||
from_file = from_project_dir / "observations.jsonl"
|
||||
if not from_file.exists():
|
||||
return 0
|
||||
|
||||
into_file = into_project_dir / "observations.jsonl"
|
||||
into_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
lines = from_file.read_text(encoding="utf-8").splitlines()
|
||||
except (OSError, UnicodeDecodeError):
|
||||
return 0
|
||||
|
||||
if not lines:
|
||||
return 0
|
||||
|
||||
with open(into_file, "a", encoding="utf-8") as f:
|
||||
for line in lines:
|
||||
if line.strip():
|
||||
f.write(line.rstrip("\n") + "\n")
|
||||
return len([line for line in lines if line.strip()])
|
||||
|
||||
|
||||
def load_all_instincts(project: dict, include_global: bool = True) -> list[dict]:
|
||||
"""Load all instincts: project-scoped + global.
|
||||
|
||||
@@ -1330,14 +1180,7 @@ def _promote_auto(project: dict, force: bool, dry_run: bool) -> int:
|
||||
# ─────────────────────────────────────────────
|
||||
|
||||
def cmd_projects(args) -> int:
|
||||
"""List or maintain known projects and their instinct counts."""
|
||||
if getattr(args, "project_action", None) == "delete":
|
||||
return _cmd_projects_delete(args)
|
||||
if getattr(args, "project_action", None) == "merge":
|
||||
return _cmd_projects_merge(args)
|
||||
if getattr(args, "project_action", None) == "gc":
|
||||
return _cmd_projects_gc(args)
|
||||
|
||||
"""List all known projects and their instinct counts."""
|
||||
registry = load_registry()
|
||||
|
||||
if not registry:
|
||||
@@ -1382,143 +1225,6 @@ def cmd_projects(args) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_projects_delete(args) -> int:
|
||||
registry = load_registry()
|
||||
project_id = args.project_id
|
||||
|
||||
if not _validate_project_id(project_id):
|
||||
print(f"Invalid project ID: {project_id}", file=sys.stderr)
|
||||
return 1
|
||||
if project_id not in registry and not (PROJECTS_DIR / project_id).exists():
|
||||
print(f"Project '{project_id}' not found.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
counts = _project_counts(project_id)
|
||||
print(f"Project: {project_id}")
|
||||
print(f" Instincts: {counts['personal']} personal, {counts['inherited']} inherited")
|
||||
print(f" Observations: {counts['observations']} events")
|
||||
|
||||
if args.dry_run:
|
||||
print(f"\n[DRY RUN] Would delete project '{project_id}' from registry and storage.")
|
||||
return 0
|
||||
|
||||
if not args.force:
|
||||
if counts["total"] > 0:
|
||||
print("\nWarning: this project has instincts or observations.")
|
||||
response = input(f"Delete project '{project_id}'? [y/N] ")
|
||||
if response.lower() != "y":
|
||||
print("Cancelled.")
|
||||
return 0
|
||||
|
||||
registry.pop(project_id, None)
|
||||
_write_registry(registry)
|
||||
_remove_project_storage(project_id)
|
||||
print(f"\nDeleted project '{project_id}'.")
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_projects_gc(args) -> int:
|
||||
registry = load_registry()
|
||||
candidates = [
|
||||
project_id
|
||||
for project_id in sorted(registry)
|
||||
if _validate_project_id(project_id) and _project_counts(project_id)["total"] == 0
|
||||
]
|
||||
|
||||
if not candidates:
|
||||
print("No zero-value project entries found.")
|
||||
return 0
|
||||
|
||||
print(f"Zero-value project entries: {len(candidates)}")
|
||||
for project_id in candidates:
|
||||
pinfo = registry.get(project_id, {})
|
||||
print(f" - {pinfo.get('name', project_id)} [{project_id}]")
|
||||
|
||||
if args.dry_run:
|
||||
print(f"\n[DRY RUN] Would delete {len(candidates)} project entr{'y' if len(candidates) == 1 else 'ies'}.")
|
||||
return 0
|
||||
|
||||
if not args.force:
|
||||
response = input(f"\nDelete {len(candidates)} zero-value project entr{'y' if len(candidates) == 1 else 'ies'}? [y/N] ")
|
||||
if response.lower() != "y":
|
||||
print("Cancelled.")
|
||||
return 0
|
||||
|
||||
for project_id in candidates:
|
||||
registry.pop(project_id, None)
|
||||
_remove_project_storage(project_id)
|
||||
_write_registry(registry)
|
||||
print(f"\nDeleted {len(candidates)} zero-value project entr{'y' if len(candidates) == 1 else 'ies'}.")
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_projects_merge(args) -> int:
|
||||
from_id = args.from_id
|
||||
into_id = args.into_id
|
||||
|
||||
if not _validate_project_id(from_id) or not _validate_project_id(into_id):
|
||||
print("Invalid project ID.", file=sys.stderr)
|
||||
return 1
|
||||
if from_id == into_id:
|
||||
print("Cannot merge a project into itself.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
registry = load_registry()
|
||||
if from_id not in registry:
|
||||
print(f"Source project '{from_id}' not found.", file=sys.stderr)
|
||||
return 1
|
||||
if into_id not in registry:
|
||||
print(f"Destination project '{into_id}' not found.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
from_counts = _project_counts(from_id)
|
||||
into_counts = _project_counts(into_id)
|
||||
print(f"Merge: {from_id} -> {into_id}")
|
||||
print(f" Source: {from_counts['personal']} personal, {from_counts['inherited']} inherited, {from_counts['observations']} observations")
|
||||
print(f" Destination before merge: {into_counts['personal']} personal, {into_counts['inherited']} inherited, {into_counts['observations']} observations")
|
||||
|
||||
if args.dry_run:
|
||||
print("\n[DRY RUN] Would merge source project into destination and remove source.")
|
||||
return 0
|
||||
|
||||
if not args.force:
|
||||
response = input(f"\nMerge '{from_id}' into '{into_id}' and remove source? [y/N] ")
|
||||
if response.lower() != "y":
|
||||
print("Cancelled.")
|
||||
return 0
|
||||
|
||||
from_project_dir = PROJECTS_DIR / from_id
|
||||
into_project_dir = PROJECTS_DIR / into_id
|
||||
into_project_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
personal_existing = _project_instinct_ids(into_project_dir, "personal")
|
||||
inherited_existing = _project_instinct_ids(into_project_dir, "inherited")
|
||||
personal_moved, personal_skipped = _merge_instinct_dir(
|
||||
from_project_dir / "instincts" / "personal",
|
||||
into_project_dir / "instincts" / "personal",
|
||||
personal_existing,
|
||||
)
|
||||
inherited_moved, inherited_skipped = _merge_instinct_dir(
|
||||
from_project_dir / "instincts" / "inherited",
|
||||
into_project_dir / "instincts" / "inherited",
|
||||
inherited_existing,
|
||||
)
|
||||
observations_moved = _append_observations(from_project_dir, into_project_dir)
|
||||
|
||||
registry.pop(from_id, None)
|
||||
destination = registry.get(into_id, {})
|
||||
destination["last_seen"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
||||
registry[into_id] = destination
|
||||
_write_registry(registry)
|
||||
_remove_project_storage(from_id)
|
||||
|
||||
print("\nMerged project registry entry.")
|
||||
print(f" Moved instincts: {personal_moved + inherited_moved}")
|
||||
print(f" Skipped duplicate instincts: {personal_skipped + inherited_skipped}")
|
||||
print(f" Appended observations: {observations_moved}")
|
||||
return 0
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────
|
||||
# Generate Evolved Structures
|
||||
# ─────────────────────────────────────────────
|
||||
@@ -1780,19 +1486,6 @@ def main() -> int:
|
||||
|
||||
# Projects (new in v2.1)
|
||||
projects_parser = subparsers.add_parser('projects', help='List known projects and instinct counts')
|
||||
projects_subparsers = projects_parser.add_subparsers(dest='project_action')
|
||||
projects_delete = projects_subparsers.add_parser('delete', help='Delete a project registry entry')
|
||||
projects_delete.add_argument('project_id', help='Project ID to delete')
|
||||
projects_delete.add_argument('--dry-run', action='store_true', help='Preview without deleting')
|
||||
projects_delete.add_argument('--force', action='store_true', help='Skip confirmation')
|
||||
projects_merge = projects_subparsers.add_parser('merge', help='Merge one project registry entry into another')
|
||||
projects_merge.add_argument('from_id', help='Source project ID')
|
||||
projects_merge.add_argument('into_id', help='Destination project ID')
|
||||
projects_merge.add_argument('--dry-run', action='store_true', help='Preview without merging')
|
||||
projects_merge.add_argument('--force', action='store_true', help='Skip confirmation')
|
||||
projects_gc = projects_subparsers.add_parser('gc', help='Delete zero-value project registry entries')
|
||||
projects_gc.add_argument('--dry-run', action='store_true', help='Preview without deleting')
|
||||
projects_gc.add_argument('--force', action='store_true', help='Skip confirmation')
|
||||
|
||||
# Prune (pending instinct TTL)
|
||||
prune_parser = subparsers.add_parser('prune', help='Delete pending instincts older than TTL')
|
||||
|
||||
@@ -177,7 +177,6 @@ test('preview pack manifest assembles release, Hermes, and publication gates', (
|
||||
'skills/hermes-imports/SKILL.md',
|
||||
'docs/architecture/harness-adapter-compliance.md',
|
||||
'scripts/preview-pack-smoke.js',
|
||||
'scripts/release-approval-gate.js',
|
||||
'docs/releases/2.0.0-rc.1/publication-readiness.md',
|
||||
'docs/releases/2.0.0-rc.1/naming-and-publication-matrix.md',
|
||||
'docs/releases/2.0.0-rc.1/release-url-ledger-2026-05-19.md',
|
||||
@@ -202,7 +201,6 @@ test('preview pack manifest assembles release, Hermes, and publication gates', (
|
||||
assert.ok(manifest.includes('no raw workspace exports'));
|
||||
assert.ok(manifest.includes('Final Verification Commands'));
|
||||
assert.ok(manifest.includes('npm run preview-pack:smoke'));
|
||||
assert.ok(manifest.includes('npm run release:approval-gate -- --format json'));
|
||||
assert.ok(manifest.includes('npm run release:video-suite -- --format json'));
|
||||
assert.ok(manifest.includes('Reference-Inspired Adapter Direction'));
|
||||
});
|
||||
@@ -231,7 +229,6 @@ test('owner approval packet consolidates the final gated decisions', () => {
|
||||
for (const command of [
|
||||
'node scripts/platform-audit.js --json',
|
||||
'npm run preview-pack:smoke -- --format json',
|
||||
'npm run release:approval-gate -- --format json',
|
||||
'npm run release:video-suite -- --format json',
|
||||
'node tests/run-all.js',
|
||||
]) {
|
||||
@@ -258,14 +255,12 @@ test('GA roadmap mirrors the current May 19 release evidence', () => {
|
||||
|
||||
for (const marker of [
|
||||
'owner-approval-packet-2026-05-19.md',
|
||||
'preview-pack smoke digest `531328aaaa53`',
|
||||
'local 2568-test suite',
|
||||
'preview-pack smoke digest `790430aef4a8`',
|
||||
'local 2550-test suite',
|
||||
'PR #2001',
|
||||
'GitHub Actions run `26102500291`',
|
||||
'PR #2002',
|
||||
'GitHub Actions run `26103853507`',
|
||||
'PR #2009',
|
||||
'GitHub Actions run `26111313938`',
|
||||
'ecc-may-19-post-pr-2002-sync-64cef8f668e0',
|
||||
'owner approval packet',
|
||||
]) {
|
||||
@@ -347,31 +342,6 @@ test('release video suite manifest gates the content launch lane', () => {
|
||||
assert.ok(packageJson.files.includes('scripts/release-video-suite.js'));
|
||||
});
|
||||
|
||||
test('release approval gate blocks publication until owner decisions and URLs are final', () => {
|
||||
const manifest = read('docs/releases/2.0.0-rc.1/preview-pack-manifest.md');
|
||||
const packet = read('docs/releases/2.0.0-rc.1/owner-approval-packet-2026-05-19.md');
|
||||
const ledger = read('docs/releases/2.0.0-rc.1/release-url-ledger-2026-05-19.md');
|
||||
const script = read('scripts/release-approval-gate.js');
|
||||
const packageJson = JSON.parse(read('package.json'));
|
||||
|
||||
for (const marker of [
|
||||
'ecc.release-approval-gate.v1',
|
||||
'owner-decisions-approved',
|
||||
'release-url-ledger-finalized',
|
||||
'announcement-copy-finalized',
|
||||
'No outbound email, personal-account post, package publish, plugin tag, or billing announcement',
|
||||
]) {
|
||||
assert.ok(script.includes(marker), `release approval gate missing ${marker}`);
|
||||
}
|
||||
|
||||
assert.ok(manifest.includes('scripts/release-approval-gate.js'));
|
||||
assert.ok(manifest.includes('npm run release:approval-gate -- --format json'));
|
||||
assert.ok(packet.includes('npm run release:approval-gate -- --format json'));
|
||||
assert.ok(ledger.includes('npm run release:approval-gate -- --format json'));
|
||||
assert.strictEqual(packageJson.scripts['release:approval-gate'], 'node scripts/release-approval-gate.js');
|
||||
assert.ok(packageJson.files.includes('scripts/release-approval-gate.js'));
|
||||
});
|
||||
|
||||
test('partner sponsor talks pack gates the hypergrowth outbound lane', () => {
|
||||
const partnerPack = read('docs/releases/2.0.0-rc.1/partner-sponsor-talks-pack.md');
|
||||
const manifest = read('docs/releases/2.0.0-rc.1/preview-pack-manifest.md');
|
||||
@@ -492,7 +462,7 @@ test('publication readiness checklist gates public release actions on evidence',
|
||||
assert.ok(source.includes('release-name-plugin-publication-checklist-2026-05-18.md'));
|
||||
assert.ok(source.includes('Release name and plugin publication checklist'));
|
||||
assert.ok(may15Evidence.includes('| Trunk discussions | GraphQL discussion count and maintainer-touch sweep | 58 total discussions;'));
|
||||
assert.ok(source.includes('platform audit sampled 59 trunk discussions'));
|
||||
assert.ok(source.includes('platform audit sampled 58 trunk discussions'));
|
||||
assert.ok(source.includes('0 needing maintainer touch'));
|
||||
assert.ok(source.includes('discussion-response-playbook.md'));
|
||||
for (const expected of [
|
||||
@@ -538,7 +508,6 @@ test('release name and plugin publication checklist freezes rc.1 surfaces', () =
|
||||
'codex plugin marketplace add --help',
|
||||
'npm publish --tag next --dry-run',
|
||||
'npm run preview-pack:smoke',
|
||||
'npm run release:approval-gate -- --format json',
|
||||
]) {
|
||||
assert.ok(checklist.includes(command), `release name/plugin checklist missing command ${command}`);
|
||||
}
|
||||
|
||||
@@ -181,14 +181,29 @@ test('detect-project.sh sets PROJECT_NAME and non-global PROJECT_ID for worktree
|
||||
}
|
||||
});
|
||||
|
||||
// Create a worktree-like directory with .git as a file
|
||||
const worktreeDir = path.join(testDir, 'my-worktree');
|
||||
execSync(`git worktree add "${worktreeDir}" -b feature/project-id`, {
|
||||
cwd: mainRepo,
|
||||
stdio: 'pipe'
|
||||
});
|
||||
assert.ok(
|
||||
fs.statSync(path.join(worktreeDir, '.git')).isFile(),
|
||||
'linked worktree should expose .git as a file'
|
||||
fs.mkdirSync(worktreeDir, { recursive: true });
|
||||
|
||||
// Set up the worktree directory structure in the main repo
|
||||
const worktreesDir = path.join(mainRepo, '.git', 'worktrees', 'my-worktree');
|
||||
fs.mkdirSync(worktreesDir, { recursive: true });
|
||||
|
||||
// Create the gitdir file and commondir in the worktree metadata
|
||||
const mainGitDir = path.join(mainRepo, '.git');
|
||||
fs.writeFileSync(
|
||||
path.join(worktreesDir, 'commondir'),
|
||||
'../..\n'
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(worktreesDir, 'HEAD'),
|
||||
fs.readFileSync(path.join(mainGitDir, 'HEAD'), 'utf8')
|
||||
);
|
||||
|
||||
// Write .git file in the worktree directory (this is what git worktree creates)
|
||||
fs.writeFileSync(
|
||||
path.join(worktreeDir, '.git'),
|
||||
`gitdir: ${worktreesDir}\n`
|
||||
);
|
||||
|
||||
// Source detect-project.sh from the worktree directory and capture results
|
||||
@@ -233,61 +248,6 @@ test('detect-project.sh sets PROJECT_NAME and non-global PROJECT_ID for worktree
|
||||
}
|
||||
});
|
||||
|
||||
test('detect-project.sh uses the main worktree hash when no remote exists', () => {
|
||||
const testDir = createTempDir();
|
||||
|
||||
try {
|
||||
const mainRepo = path.join(testDir, 'main-repo');
|
||||
const worktreeDir = path.join(testDir, 'feature-worktree');
|
||||
const homeDir = path.join(testDir, 'home');
|
||||
fs.mkdirSync(mainRepo, { recursive: true });
|
||||
fs.mkdirSync(homeDir, { recursive: true });
|
||||
execSync('git init', { cwd: mainRepo, stdio: 'pipe' });
|
||||
execSync('git commit --allow-empty -m "init"', {
|
||||
cwd: mainRepo,
|
||||
stdio: 'pipe',
|
||||
env: {
|
||||
...process.env,
|
||||
GIT_AUTHOR_NAME: 'Test',
|
||||
GIT_AUTHOR_EMAIL: 'test@test.com',
|
||||
GIT_COMMITTER_NAME: 'Test',
|
||||
GIT_COMMITTER_EMAIL: 'test@test.com'
|
||||
}
|
||||
});
|
||||
execSync(`git worktree add "${worktreeDir}" -b feature/no-remote`, {
|
||||
cwd: mainRepo,
|
||||
stdio: 'pipe'
|
||||
});
|
||||
|
||||
function detectId(targetDir) {
|
||||
const script = `
|
||||
export HOME="${toBashPath(homeDir)}"
|
||||
export USERPROFILE="${toBashPath(homeDir)}"
|
||||
export CLAUDE_PROJECT_DIR="${toBashPath(targetDir)}"
|
||||
source "${toBashPath(detectProjectPath)}" >/dev/null
|
||||
printf "%s" "$PROJECT_ID"
|
||||
`;
|
||||
return execFileSync('bash', ['-lc', script], {
|
||||
cwd: targetDir,
|
||||
timeout: 10000,
|
||||
env: {
|
||||
...process.env,
|
||||
HOME: toBashPath(homeDir),
|
||||
USERPROFILE: toBashPath(homeDir),
|
||||
CLAUDE_PROJECT_DIR: toBashPath(targetDir)
|
||||
}
|
||||
}).toString();
|
||||
}
|
||||
|
||||
const mainId = detectId(mainRepo);
|
||||
const worktreeId = detectId(worktreeDir);
|
||||
assert.ok(mainId && mainId !== 'global', 'main repo should get a project id');
|
||||
assert.strictEqual(worktreeId, mainId, 'linked worktree should share the main worktree project id');
|
||||
} finally {
|
||||
cleanupDir(testDir);
|
||||
}
|
||||
});
|
||||
|
||||
// ──────────────────────────────────────────────────────
|
||||
// Summary
|
||||
// ──────────────────────────────────────────────────────
|
||||
|
||||
@@ -769,8 +769,6 @@ function runTests() {
|
||||
'git diff --name-only',
|
||||
'git log --oneline --max-count=1',
|
||||
'git show HEAD:README.md',
|
||||
'git show HEAD:"docs/install guide.md"',
|
||||
'/usr/bin/git status --short',
|
||||
'git branch --show-current',
|
||||
'git rev-parse --abbrev-ref HEAD',
|
||||
];
|
||||
@@ -804,20 +802,7 @@ function runTests() {
|
||||
assert.ok(output.hookSpecificOutput.permissionDecisionReason.includes('current user request'));
|
||||
})) passed++; else failed++;
|
||||
|
||||
// --- Test 23: quoted shell separators are not read-only git bypasses
|
||||
clearState();
|
||||
if (test('does not treat quoted shell separators as read-only git introspection', () => {
|
||||
const result = runBashHook({
|
||||
tool_name: 'Bash',
|
||||
tool_input: { command: 'git show HEAD:"docs/a;b.md"' }
|
||||
});
|
||||
const output = parseOutput(result.stdout);
|
||||
assert.ok(output, 'should produce valid JSON output');
|
||||
assert.strictEqual(output.hookSpecificOutput.permissionDecision, 'deny');
|
||||
assert.ok(output.hookSpecificOutput.permissionDecisionReason.includes('current user request'));
|
||||
})) passed++; else failed++;
|
||||
|
||||
// --- Test 24: module-load pruning removes old state files only ---
|
||||
// --- Test 23: module-load pruning removes old state files only ---
|
||||
clearState();
|
||||
if (test('prunes stale state files while keeping fresh state files', () => {
|
||||
const staleFile = path.join(stateDir, 'state-stale-session.json');
|
||||
|
||||
@@ -3300,14 +3300,11 @@ async function runTests() {
|
||||
|
||||
assert.strictEqual(result.code, 0, `observe.sh should exit successfully, stderr: ${result.stderr}`);
|
||||
|
||||
const homunculusDir = path.join(homeDir, '.local', 'share', 'ecc-homunculus');
|
||||
const projectsDir = path.join(homunculusDir, 'projects');
|
||||
assert.ok(
|
||||
!fs.existsSync(projectsDir) || fs.readdirSync(projectsDir).length === 0,
|
||||
'observe.sh should not create a project-scoped directory for a non-git cwd'
|
||||
);
|
||||
const projectsDir = path.join(homeDir, '.local', 'share', 'ecc-homunculus', 'projects');
|
||||
const projectIds = fs.readdirSync(projectsDir);
|
||||
assert.strictEqual(projectIds.length, 1, 'observe.sh should create one project-scoped observation directory');
|
||||
|
||||
const observationsPath = path.join(homunculusDir, 'observations.jsonl');
|
||||
const observationsPath = path.join(projectsDir, projectIds[0], 'observations.jsonl');
|
||||
const observations = fs.readFileSync(observationsPath, 'utf8').trim().split('\n').filter(Boolean);
|
||||
assert.ok(observations.length > 0, 'observe.sh should append at least one observation');
|
||||
|
||||
|
||||
@@ -135,8 +135,8 @@ test('observe.sh resolves cwd to git root before setting CLAUDE_PROJECT_DIR', ()
|
||||
'observe.sh should resolve STDIN_CWD to git repo root'
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('export CLV2_NO_PROJECT=1'),
|
||||
'observe.sh should mark non-git cwd payloads as global instead of registering raw cwd'
|
||||
content.includes('${_GIT_ROOT:-$STDIN_CWD}'),
|
||||
'observe.sh should fall back to raw cwd when git root is unavailable'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -250,7 +250,7 @@ test('observe.sh falls back to CLAUDE_HOOK_EVENT_NAME when no phase argument is
|
||||
}
|
||||
});
|
||||
|
||||
test('observe.sh records non-git cwd payloads globally without project registry side effects', () => {
|
||||
test('observe.sh keeps the raw cwd when the directory is not inside a git repo', () => {
|
||||
const testRoot = createTempDir();
|
||||
|
||||
try {
|
||||
@@ -262,17 +262,12 @@ test('observe.sh records non-git cwd payloads globally without project registry
|
||||
const result = runObserve({ homeDir, cwd: nonGitDir });
|
||||
assert.strictEqual(result.status, 0, result.stderr);
|
||||
|
||||
const homunculusDir = path.join(homeDir, '.local', 'share', 'ecc-homunculus');
|
||||
const projectsDir = path.join(homunculusDir, 'projects');
|
||||
const registryPath = path.join(homunculusDir, 'projects.json');
|
||||
const observationsPath = path.join(homunculusDir, 'observations.jsonl');
|
||||
|
||||
assert.ok(!fs.existsSync(registryPath), 'non-git cwd should not create projects.json');
|
||||
assert.ok(
|
||||
!fs.existsSync(projectsDir) || fs.readdirSync(projectsDir).length === 0,
|
||||
'non-git cwd should not create project directories'
|
||||
const { metadata } = readSingleProjectMetadata(homeDir);
|
||||
assert.strictEqual(
|
||||
normalizeComparablePath(metadata.root),
|
||||
normalizeComparablePath(nonGitDir),
|
||||
'project metadata root should stay on the non-git cwd'
|
||||
);
|
||||
assert.ok(fs.existsSync(observationsPath), 'non-git cwd should still record a global observation');
|
||||
} finally {
|
||||
cleanupDir(testRoot);
|
||||
}
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
const repoRoot = path.resolve(__dirname, '..', '..');
|
||||
const cliPath = path.join(
|
||||
repoRoot,
|
||||
'skills',
|
||||
'continuous-learning-v2',
|
||||
'scripts',
|
||||
'instinct-cli.py'
|
||||
);
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
passed += 1;
|
||||
} catch (error) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function createTempDir() {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-instinct-cli-projects-'));
|
||||
}
|
||||
|
||||
function cleanupDir(dir) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function writeJson(filePath, payload) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`);
|
||||
}
|
||||
|
||||
function readJson(filePath) {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
|
||||
function writeInstinct(filePath, id, confidence = 0.9) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
[
|
||||
'---',
|
||||
`id: ${id}`,
|
||||
'trigger: "when repeated"',
|
||||
`confidence: ${confidence}`,
|
||||
'domain: workflow',
|
||||
'---',
|
||||
'',
|
||||
`Action for ${id}.`,
|
||||
'',
|
||||
].join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
function seedProject(root, id, options = {}) {
|
||||
const projectDir = path.join(root, 'projects', id);
|
||||
const personalDir = path.join(projectDir, 'instincts', 'personal');
|
||||
const inheritedDir = path.join(projectDir, 'instincts', 'inherited');
|
||||
fs.mkdirSync(personalDir, { recursive: true });
|
||||
fs.mkdirSync(inheritedDir, { recursive: true });
|
||||
|
||||
for (const instinct of options.personal || []) {
|
||||
writeInstinct(path.join(personalDir, `${instinct}.yaml`), instinct);
|
||||
}
|
||||
for (const instinct of options.inherited || []) {
|
||||
writeInstinct(path.join(inheritedDir, `${instinct}.yaml`), instinct);
|
||||
}
|
||||
if (options.observations) {
|
||||
fs.writeFileSync(
|
||||
path.join(projectDir, 'observations.jsonl'),
|
||||
options.observations.map(row => JSON.stringify(row)).join('\n') + '\n'
|
||||
);
|
||||
}
|
||||
|
||||
return projectDir;
|
||||
}
|
||||
|
||||
function projectHash(value) {
|
||||
return crypto.createHash('sha256').update(value).digest('hex').slice(0, 12);
|
||||
}
|
||||
|
||||
function runGit(cwd, args) {
|
||||
const result = spawnSync('git', args, {
|
||||
cwd,
|
||||
encoding: 'utf8',
|
||||
});
|
||||
assert.strictEqual(result.status, 0, result.stderr);
|
||||
return result.stdout.trim();
|
||||
}
|
||||
|
||||
function runCli(root, args, options = {}) {
|
||||
return spawnSync('python3', [cliPath, ...args], {
|
||||
cwd: options.cwd || repoRoot,
|
||||
encoding: 'utf8',
|
||||
env: {
|
||||
...process.env,
|
||||
CLV2_HOMUNCULUS_DIR: root,
|
||||
HOME: path.join(root, 'home'),
|
||||
USERPROFILE: path.join(root, 'home'),
|
||||
CLAUDE_PROJECT_DIR: '',
|
||||
...(options.env || {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log('\n=== Testing instinct-cli.py projects maintenance ===\n');
|
||||
|
||||
test('projects delete --dry-run preserves registry and project files', () => {
|
||||
const root = createTempDir();
|
||||
try {
|
||||
const registryPath = path.join(root, 'projects.json');
|
||||
seedProject(root, 'alpha123', {
|
||||
personal: ['keep-me'],
|
||||
observations: [{ event: 'tool_complete' }],
|
||||
});
|
||||
writeJson(registryPath, {
|
||||
alpha123: { name: 'alpha', root: '/repo/alpha', remote: '', last_seen: '2026-01-01T00:00:00Z' },
|
||||
});
|
||||
|
||||
const result = runCli(root, ['projects', 'delete', 'alpha123', '--dry-run']);
|
||||
assert.strictEqual(result.status, 0, result.stderr);
|
||||
assert.match(result.stdout, /would delete/i);
|
||||
assert.ok(fs.existsSync(path.join(root, 'projects', 'alpha123')));
|
||||
assert.ok(readJson(registryPath).alpha123);
|
||||
} finally {
|
||||
cleanupDir(root);
|
||||
}
|
||||
});
|
||||
|
||||
test('projects delete --force removes registry entry and project directory', () => {
|
||||
const root = createTempDir();
|
||||
try {
|
||||
const registryPath = path.join(root, 'projects.json');
|
||||
seedProject(root, 'alpha123', { personal: ['delete-me'] });
|
||||
writeJson(registryPath, {
|
||||
alpha123: { name: 'alpha', root: '/repo/alpha', remote: '', last_seen: '2026-01-01T00:00:00Z' },
|
||||
});
|
||||
|
||||
const result = runCli(root, ['projects', 'delete', 'alpha123', '--force']);
|
||||
assert.strictEqual(result.status, 0, result.stderr);
|
||||
assert.ok(!fs.existsSync(path.join(root, 'projects', 'alpha123')));
|
||||
assert.ok(!readJson(registryPath).alpha123);
|
||||
} finally {
|
||||
cleanupDir(root);
|
||||
}
|
||||
});
|
||||
|
||||
test('projects gc --force removes only zero-value project entries', () => {
|
||||
const root = createTempDir();
|
||||
try {
|
||||
const registryPath = path.join(root, 'projects.json');
|
||||
seedProject(root, 'empty000');
|
||||
seedProject(root, 'active999', { personal: ['active'] });
|
||||
writeJson(registryPath, {
|
||||
empty000: { name: 'empty', root: '/tmp/empty', remote: '', last_seen: '2026-01-01T00:00:00Z' },
|
||||
active999: { name: 'active', root: '/repo/active', remote: '', last_seen: '2026-01-02T00:00:00Z' },
|
||||
});
|
||||
|
||||
const result = runCli(root, ['projects', 'gc', '--force']);
|
||||
assert.strictEqual(result.status, 0, result.stderr);
|
||||
const registry = readJson(registryPath);
|
||||
assert.ok(!registry.empty000);
|
||||
assert.ok(registry.active999);
|
||||
assert.ok(!fs.existsSync(path.join(root, 'projects', 'empty000')));
|
||||
assert.ok(fs.existsSync(path.join(root, 'projects', 'active999')));
|
||||
} finally {
|
||||
cleanupDir(root);
|
||||
}
|
||||
});
|
||||
|
||||
test('projects merge deduplicates instincts, appends observations, and removes source', () => {
|
||||
const root = createTempDir();
|
||||
try {
|
||||
const registryPath = path.join(root, 'projects.json');
|
||||
seedProject(root, 'from111', {
|
||||
personal: ['shared', 'from-only'],
|
||||
observations: [{ event: 'from-event' }],
|
||||
});
|
||||
seedProject(root, 'into222', {
|
||||
personal: ['shared', 'into-only'],
|
||||
observations: [{ event: 'into-event' }],
|
||||
});
|
||||
writeJson(registryPath, {
|
||||
from111: { name: 'from', root: '/repo/from', remote: '', last_seen: '2026-01-01T00:00:00Z' },
|
||||
into222: { name: 'into', root: '/repo/into', remote: '', last_seen: '2026-01-02T00:00:00Z' },
|
||||
});
|
||||
|
||||
const result = runCli(root, ['projects', 'merge', 'from111', 'into222', '--force']);
|
||||
assert.strictEqual(result.status, 0, result.stderr);
|
||||
assert.ok(!fs.existsSync(path.join(root, 'projects', 'from111')));
|
||||
assert.ok(!readJson(registryPath).from111);
|
||||
assert.ok(readJson(registryPath).into222);
|
||||
|
||||
const intoPersonal = path.join(root, 'projects', 'into222', 'instincts', 'personal');
|
||||
assert.ok(fs.existsSync(path.join(intoPersonal, 'shared.yaml')));
|
||||
assert.ok(fs.existsSync(path.join(intoPersonal, 'from-only.yaml')));
|
||||
assert.ok(fs.existsSync(path.join(intoPersonal, 'into-only.yaml')));
|
||||
|
||||
const observations = fs.readFileSync(
|
||||
path.join(root, 'projects', 'into222', 'observations.jsonl'),
|
||||
'utf8'
|
||||
);
|
||||
assert.match(observations, /from-event/);
|
||||
assert.match(observations, /into-event/);
|
||||
} finally {
|
||||
cleanupDir(root);
|
||||
}
|
||||
});
|
||||
|
||||
test('status migrates legacy no-remote linked worktree project dirs to main worktree id', () => {
|
||||
const root = createTempDir();
|
||||
const repoParent = createTempDir();
|
||||
try {
|
||||
const mainWorktree = path.join(repoParent, 'main');
|
||||
const linkedWorktree = path.join(repoParent, 'linked');
|
||||
fs.mkdirSync(mainWorktree, { recursive: true });
|
||||
runGit(mainWorktree, ['init']);
|
||||
runGit(mainWorktree, ['config', 'user.email', 'ecc@example.test']);
|
||||
runGit(mainWorktree, ['config', 'user.name', 'ECC Test']);
|
||||
fs.writeFileSync(path.join(mainWorktree, 'README.md'), 'test\n');
|
||||
runGit(mainWorktree, ['add', 'README.md']);
|
||||
runGit(mainWorktree, ['commit', '-m', 'init']);
|
||||
runGit(mainWorktree, ['worktree', 'add', linkedWorktree]);
|
||||
|
||||
const mainRoot = runGit(mainWorktree, ['rev-parse', '--show-toplevel']);
|
||||
const linkedRoot = runGit(linkedWorktree, ['rev-parse', '--show-toplevel']);
|
||||
const oldLinkedId = projectHash(linkedRoot);
|
||||
const mainId = projectHash(mainRoot);
|
||||
seedProject(root, oldLinkedId, { personal: ['legacy-worktree'] });
|
||||
|
||||
const result = runCli(root, ['status'], { cwd: linkedRoot });
|
||||
assert.strictEqual(result.status, 0, result.stderr);
|
||||
assert.ok(!fs.existsSync(path.join(root, 'projects', oldLinkedId)));
|
||||
assert.ok(fs.existsSync(path.join(root, 'projects', mainId)));
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(root, 'projects', mainId, 'instincts', 'personal', 'legacy-worktree.yaml'))
|
||||
);
|
||||
assert.match(result.stdout, new RegExp(`\\(${mainId}\\)`));
|
||||
} finally {
|
||||
cleanupDir(root);
|
||||
cleanupDir(repoParent);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\nPassed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
@@ -60,7 +60,6 @@ function buildExpectedPublishPaths(repoRoot) {
|
||||
"scripts/operator-readiness-dashboard.js",
|
||||
"scripts/platform-audit.js",
|
||||
"scripts/preview-pack-smoke.js",
|
||||
"scripts/release-approval-gate.js",
|
||||
"scripts/release-video-suite.js",
|
||||
"scripts/skill-create-output.js",
|
||||
"scripts/repair.js",
|
||||
@@ -133,7 +132,6 @@ function main() {
|
||||
"scripts/discussion-audit.js",
|
||||
"scripts/operator-readiness-dashboard.js",
|
||||
"scripts/preview-pack-smoke.js",
|
||||
"scripts/release-approval-gate.js",
|
||||
"scripts/release-video-suite.js",
|
||||
"scripts/work-items.js",
|
||||
"scripts/platform-audit.js",
|
||||
|
||||
@@ -77,9 +77,6 @@ function seedRepo(rootDir, overrides = {}) {
|
||||
'42653f9',
|
||||
'target account billing readback',
|
||||
'632e059',
|
||||
'select-ready-target',
|
||||
'f14ed2fe-a219-470c-8119-63429e197027',
|
||||
'old "no Marketplace-managed Pro target billing-state" blocker is cleared',
|
||||
'69ca535',
|
||||
'team feedback controls',
|
||||
'e56fc1a',
|
||||
@@ -193,7 +190,7 @@ function seedRepo(rootDir, overrides = {}) {
|
||||
'Operator dashboard',
|
||||
'GitGuardian',
|
||||
'macOS/Ubuntu/Windows test matrix',
|
||||
'2568 passed',
|
||||
'2550 passed',
|
||||
'Business baseline',
|
||||
'$1,728/mo',
|
||||
'$8,272/mo'
|
||||
@@ -350,7 +347,7 @@ function runTests() {
|
||||
)));
|
||||
assert.ok(report.requirements.some(item => (
|
||||
item.id === 'ecc-tools-next-level'
|
||||
&& item.gap === 'obtain or rotate the local/internal INTERNAL_API_SECRET bearer-token path, then run the live billing announcement gate for the selected Marketplace Pro target before publishing native-payments copy'
|
||||
&& item.gap === 'authorize Cloudflare API or 1Password CLI access, configure the target Marketplace Pro account and INTERNAL_API_SECRET, create or replay Marketplace Pro webhook state, then rerun target readback and the live announcement gate'
|
||||
&& item.evidence.includes('operator-visible promotion output details')
|
||||
&& item.evidence.includes('hosted promotion judge audit traces')
|
||||
&& item.evidence.includes('billing announcement preflight')
|
||||
@@ -358,7 +355,6 @@ function runTests() {
|
||||
&& item.evidence.includes('Wrangler OAuth readback')
|
||||
&& item.evidence.includes('target-account billing readback')
|
||||
&& item.evidence.includes('provenance-aware Marketplace billing-state gates')
|
||||
&& item.evidence.includes('ready Marketplace Pro target selection')
|
||||
&& item.evidence.includes('hosted team-learning feedback controls')
|
||||
&& item.evidence.includes('ECC-Tools Dependabot alert remediation')
|
||||
)));
|
||||
@@ -447,7 +443,7 @@ function runTests() {
|
||||
'Operator dashboard',
|
||||
'GitGuardian',
|
||||
'macOS/Ubuntu/Windows test matrix',
|
||||
'2568 passed',
|
||||
'2550 passed',
|
||||
'Business baseline',
|
||||
'$1,728/mo',
|
||||
'$8,272/mo',
|
||||
|
||||
@@ -68,7 +68,7 @@ function seedRepo(rootDir, overrides = {}) {
|
||||
'Operator dashboard',
|
||||
'GitGuardian',
|
||||
'macOS/Ubuntu/Windows test matrix',
|
||||
'2568 passed'
|
||||
'2550 passed'
|
||||
].join('\n'),
|
||||
'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-19.md': [
|
||||
'This dashboard is generated by `npm run operator:dashboard`',
|
||||
@@ -238,7 +238,7 @@ function runTests() {
|
||||
'Operator dashboard',
|
||||
'GitGuardian',
|
||||
'macOS/Ubuntu/Windows test matrix',
|
||||
'2560 passed'
|
||||
'2546 passed'
|
||||
].join('\n')
|
||||
});
|
||||
|
||||
|
||||
@@ -1,320 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { execFileSync, spawnSync } = require('child_process');
|
||||
|
||||
const SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'release-approval-gate.js');
|
||||
const {
|
||||
REQUIRED_DECISIONS,
|
||||
REQUIRED_URL_SURFACES,
|
||||
buildReport,
|
||||
parseArgs,
|
||||
renderText,
|
||||
} = require(SCRIPT);
|
||||
|
||||
const RELEASE_DIR = 'docs/releases/2.0.0-rc.1';
|
||||
|
||||
function createTempDir(prefix) {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
}
|
||||
|
||||
function cleanup(dirPath) {
|
||||
fs.rmSync(dirPath, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function writeFile(rootDir, relativePath, content) {
|
||||
const targetPath = path.join(rootDir, relativePath);
|
||||
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
||||
fs.writeFileSync(targetPath, content);
|
||||
}
|
||||
|
||||
function approvedPacketContent(overrides = {}) {
|
||||
const decisions = new Map(REQUIRED_DECISIONS.map(decision => [decision.label, 'approve']));
|
||||
for (const [label, value] of Object.entries(overrides)) {
|
||||
decisions.set(label, value);
|
||||
}
|
||||
|
||||
return [
|
||||
'# ECC v2.0.0-rc.1 Owner Approval Packet',
|
||||
'',
|
||||
'## Decision Register',
|
||||
'',
|
||||
'| Decision | Approve / defer / block | Evidence required first | Notes |',
|
||||
'| --- | --- | --- | --- |',
|
||||
...REQUIRED_DECISIONS.map(decision => (
|
||||
`| ${decision.label} | ${decisions.get(decision.label)} | final evidence | approved fixture |`
|
||||
)),
|
||||
'',
|
||||
'## Final Evidence Commands',
|
||||
'',
|
||||
'```bash',
|
||||
'npm run release:approval-gate -- --format json',
|
||||
'```',
|
||||
'',
|
||||
'No outbound email, personal-account post, package publish, plugin tag, or billing announcement is authorized by this packet alone.',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function finalLedgerContent(extra = '') {
|
||||
return [
|
||||
'# ECC v2.0.0-rc.1 Release URL Ledger',
|
||||
'',
|
||||
'## Final Published URLs',
|
||||
'',
|
||||
'| Surface | URL | Verification |',
|
||||
'| --- | --- | --- |',
|
||||
...REQUIRED_URL_SURFACES.map(surface => (
|
||||
`| ${surface.label} | ${surface.exampleUrl} | readback from final release commit |`
|
||||
)),
|
||||
'',
|
||||
'## Final Verification Commands',
|
||||
'',
|
||||
'```bash',
|
||||
'npm run release:approval-gate -- --format json',
|
||||
'```',
|
||||
'',
|
||||
extra,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function manifestContent() {
|
||||
return [
|
||||
'# ECC v2.0.0-rc.1 Preview Pack Manifest',
|
||||
'',
|
||||
'| Artifact | Role | Gate |',
|
||||
'| --- | --- | --- |',
|
||||
'| `scripts/release-approval-gate.js` | Final owner approval and live URL gate | Verified by `npm run release:approval-gate -- --format json` |',
|
||||
'',
|
||||
'## Final Verification Commands',
|
||||
'',
|
||||
'```bash',
|
||||
'npm run release:approval-gate -- --format json',
|
||||
'```',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function seedRepo(rootDir, overrides = {}) {
|
||||
const files = {
|
||||
'package.json': JSON.stringify({
|
||||
files: ['scripts/release-approval-gate.js'],
|
||||
scripts: {
|
||||
'release:approval-gate': 'node scripts/release-approval-gate.js',
|
||||
},
|
||||
}, null, 2),
|
||||
'scripts/release-approval-gate.js': 'release approval gate script',
|
||||
[`${RELEASE_DIR}/owner-approval-packet-2026-05-19.md`]: approvedPacketContent(),
|
||||
[`${RELEASE_DIR}/release-url-ledger-2026-05-19.md`]: finalLedgerContent(),
|
||||
[`${RELEASE_DIR}/preview-pack-manifest.md`]: manifestContent(),
|
||||
[`${RELEASE_DIR}/release-notes.md`]: 'Release notes with final URLs.',
|
||||
[`${RELEASE_DIR}/x-thread.md`]: 'X post with final URLs.',
|
||||
[`${RELEASE_DIR}/linkedin-post.md`]: 'LinkedIn post with final URLs.',
|
||||
[`${RELEASE_DIR}/article-outline.md`]: 'Article outline with final URLs.',
|
||||
[`${RELEASE_DIR}/partner-sponsor-talks-pack.md`]: 'Outbound copy with final URLs.',
|
||||
'docs/business/social-launch-copy.md': 'Business launch copy with final URLs.',
|
||||
};
|
||||
|
||||
for (const [relativePath, content] of Object.entries({ ...files, ...overrides })) {
|
||||
if (content === null) {
|
||||
continue;
|
||||
}
|
||||
writeFile(rootDir, relativePath, content);
|
||||
}
|
||||
}
|
||||
|
||||
function run(args = [], options = {}) {
|
||||
return execFileSync('node', [SCRIPT, ...args], {
|
||||
cwd: options.cwd || path.join(__dirname, '..', '..'),
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 10000,
|
||||
});
|
||||
}
|
||||
|
||||
function runProcess(args = [], options = {}) {
|
||||
return spawnSync('node', [SCRIPT, ...args], {
|
||||
cwd: options.cwd || path.join(__dirname, '..', '..'),
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 10000,
|
||||
});
|
||||
}
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` PASS ${name}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(` FAIL ${name}`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing release-approval-gate.js ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
if (test('parseArgs accepts approval gate flags and rejects invalid values', () => {
|
||||
const rootDir = createTempDir('release-approval-args-');
|
||||
|
||||
try {
|
||||
const parsed = parseArgs([
|
||||
'node',
|
||||
'script',
|
||||
'--format=json',
|
||||
`--root=${rootDir}`,
|
||||
]);
|
||||
|
||||
assert.strictEqual(parsed.format, 'json');
|
||||
assert.strictEqual(parsed.root, path.resolve(rootDir));
|
||||
assert.throws(() => parseArgs(['node', 'script', '--format', 'xml']), /Invalid format/);
|
||||
assert.throws(() => parseArgs(['node', 'script', '--root']), /--root requires a value/);
|
||||
assert.throws(() => parseArgs(['node', 'script', '--unknown']), /Unknown argument/);
|
||||
} finally {
|
||||
cleanup(rootDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('seeded approved release passes every publication approval check', () => {
|
||||
const rootDir = createTempDir('release-approval-pass-');
|
||||
|
||||
try {
|
||||
seedRepo(rootDir);
|
||||
const report = buildReport({ root: rootDir });
|
||||
|
||||
assert.strictEqual(report.schema_version, 'ecc.release-approval-gate.v1');
|
||||
assert.strictEqual(report.ready, true);
|
||||
assert.strictEqual(report.summary.failed, 0);
|
||||
assert.deepStrictEqual(report.top_actions, []);
|
||||
assert.ok(report.checks.every(check => check.status === 'pass'));
|
||||
|
||||
const text = renderText(report);
|
||||
assert.ok(text.includes('Ready: yes'));
|
||||
assert.ok(text.includes('Failed: 0'));
|
||||
} finally {
|
||||
cleanup(rootDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('deferred owner decisions keep the publication gate blocked', () => {
|
||||
const rootDir = createTempDir('release-approval-deferred-');
|
||||
|
||||
try {
|
||||
seedRepo(rootDir, {
|
||||
[`${RELEASE_DIR}/owner-approval-packet-2026-05-19.md`]: approvedPacketContent({
|
||||
'GitHub prerelease': 'defer',
|
||||
'Sponsor, partner, consulting, conference, podcast outreach': 'block',
|
||||
}),
|
||||
});
|
||||
|
||||
const report = buildReport({ root: rootDir });
|
||||
const decisions = report.checks.find(check => check.id === 'owner-decisions-approved');
|
||||
|
||||
assert.strictEqual(report.ready, false);
|
||||
assert.strictEqual(decisions.status, 'fail');
|
||||
assert.ok(decisions.evidence.includes('GitHub prerelease=defer'));
|
||||
assert.ok(decisions.evidence.includes('Sponsor, partner, consulting, conference, podcast outreach=block'));
|
||||
assert.ok(report.top_actions.some(action => action.includes('Approve, defer, or block')));
|
||||
} finally {
|
||||
cleanup(rootDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('approval-gated URL ledger rows keep the publication gate blocked', () => {
|
||||
const rootDir = createTempDir('release-approval-ledger-');
|
||||
|
||||
try {
|
||||
seedRepo(rootDir, {
|
||||
[`${RELEASE_DIR}/release-url-ledger-2026-05-19.md`]: [
|
||||
'# ECC v2.0.0-rc.1 Release URL Ledger',
|
||||
'',
|
||||
'## Approval-Gated URLs',
|
||||
'',
|
||||
'| Surface | Intended URL or command | Gate before use |',
|
||||
'| --- | --- | --- |',
|
||||
'| GitHub prerelease | https://github.com/affaan-m/ECC/releases/tag/v2.0.0-rc.1 | must return the prerelease |',
|
||||
].join('\n'),
|
||||
});
|
||||
|
||||
const report = buildReport({ root: rootDir });
|
||||
const ledger = report.checks.find(check => check.id === 'release-url-ledger-finalized');
|
||||
|
||||
assert.strictEqual(report.ready, false);
|
||||
assert.strictEqual(ledger.status, 'fail');
|
||||
assert.ok(ledger.evidence.includes('approval-gated URL section still present'));
|
||||
} finally {
|
||||
cleanup(rootDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('announcement drafts fail on unresolved placeholders and private paths', () => {
|
||||
const rootDir = createTempDir('release-approval-copy-');
|
||||
|
||||
try {
|
||||
seedRepo(rootDir, {
|
||||
[`${RELEASE_DIR}/x-thread.md`]: 'Ship copy with <video-url> and /Users/affaan/raw-footage.',
|
||||
});
|
||||
|
||||
const report = buildReport({ root: rootDir });
|
||||
const copy = report.checks.find(check => check.id === 'announcement-copy-finalized');
|
||||
|
||||
assert.strictEqual(report.ready, false);
|
||||
assert.strictEqual(copy.status, 'fail');
|
||||
assert.ok(copy.evidence.includes(`${RELEASE_DIR}/x-thread.md:1`));
|
||||
} finally {
|
||||
cleanup(rootDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('CLI emits json and uses status 2 for blocked approval reports', () => {
|
||||
const rootDir = createTempDir('release-approval-cli-');
|
||||
|
||||
try {
|
||||
seedRepo(rootDir);
|
||||
const stdout = run(['--format=json', `--root=${rootDir}`], { cwd: rootDir });
|
||||
const parsed = JSON.parse(stdout);
|
||||
assert.strictEqual(parsed.ready, true);
|
||||
|
||||
writeFile(
|
||||
rootDir,
|
||||
`${RELEASE_DIR}/owner-approval-packet-2026-05-19.md`,
|
||||
approvedPacketContent({ 'Video upload': 'defer' })
|
||||
);
|
||||
const failedRun = runProcess(['--format=json', `--root=${rootDir}`], { cwd: rootDir });
|
||||
assert.strictEqual(failedRun.status, 2);
|
||||
assert.strictEqual(failedRun.stderr, '');
|
||||
assert.ok(failedRun.stdout.includes('"ready": false'));
|
||||
} finally {
|
||||
cleanup(rootDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('CLI help exits successfully and invalid flags fail before reporting', () => {
|
||||
const help = runProcess(['--help']);
|
||||
assert.strictEqual(help.status, 0);
|
||||
assert.strictEqual(help.stderr, '');
|
||||
assert.ok(help.stdout.includes('Usage: node scripts/release-approval-gate.js'));
|
||||
|
||||
const invalid = runProcess(['--format=xml']);
|
||||
assert.strictEqual(invalid.status, 1);
|
||||
assert.strictEqual(invalid.stdout, '');
|
||||
assert.match(invalid.stderr, /Error: Invalid format/);
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nPassed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
|
||||
if (failed > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
runTests();
|
||||
}
|
||||
32
yarn.lock
32
yarn.lock
@@ -302,12 +302,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:25.7.0":
|
||||
version: 25.7.0
|
||||
resolution: "@types/node@npm:25.7.0"
|
||||
"@types/node@npm:^25.8.0":
|
||||
version: 25.8.0
|
||||
resolution: "@types/node@npm:25.8.0"
|
||||
dependencies:
|
||||
undici-types: "npm:~7.21.0"
|
||||
checksum: 10c0/47ec7eaca154c36ad6d1ac0270e6e254eedf20b9dc49afe3bc76e4f7eba29ceac705f8903b162aeaf40e3941101ffe76ffb374989359ea3ef8c8509d8b443f55
|
||||
undici-types: "npm:>=7.24.0 <7.24.7"
|
||||
checksum: 10c0/ff53e5428309d2e6060190ec5e02afd0e4a7369456b16130a7f5898f12a6ad0efd62d752830f2f7355d714ae429bc0acbb2dc0cbf761cadb03e88c4996cdf1dc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -412,21 +412,21 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"brace-expansion@npm:^1.1.7":
|
||||
version: 1.1.14
|
||||
resolution: "brace-expansion@npm:1.1.14"
|
||||
version: 1.1.13
|
||||
resolution: "brace-expansion@npm:1.1.13"
|
||||
dependencies:
|
||||
balanced-match: "npm:^1.0.0"
|
||||
concat-map: "npm:0.0.1"
|
||||
checksum: 10c0/b6fdac832bc4e36a753658c9ed052c2e1a2be221763b002df25d1efbf7d21724334e726a6cd5eadc72a4b19ec3efb632d629cc003bc9c62f7af7a7915ffa4385
|
||||
checksum: 10c0/384c61bb329b6adfdcc0cbbdd108dc19fb5f3e84ae15a02a74f94c6c791b5a9b035aae73b2a51929a8a478e2f0f212a771eb6a8b5b514cccfb8d0c9f2ce8cbd8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"brace-expansion@npm:^5.0.5":
|
||||
version: 5.0.6
|
||||
resolution: "brace-expansion@npm:5.0.6"
|
||||
version: 5.0.5
|
||||
resolution: "brace-expansion@npm:5.0.5"
|
||||
dependencies:
|
||||
balanced-match: "npm:^4.0.2"
|
||||
checksum: 10c0/8c919869b90f61d533b341d3340be5ee4413232ea89b8246cbc2f38eb014f1d8182785c98a006eaf6111d02dc9eeffefdc240d5ac158625b2ed084dccd4bbf9b
|
||||
checksum: 10c0/4d238e14ed4f5cc9c07285550a41cef23121ca08ba99fa9eb5b55b580dcb6bf868b8210aa10526bdc9f8dc97f33ca2a7259039c4cc131a93042beddb424c48e3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -632,7 +632,7 @@ __metadata:
|
||||
"@eslint/js": "npm:^9.39.2"
|
||||
"@iarna/toml": "npm:^2.2.5"
|
||||
"@opencode-ai/plugin": "npm:^1.0.0"
|
||||
"@types/node": "npm:25.7.0"
|
||||
"@types/node": "npm:^25.8.0"
|
||||
ajv: "npm:^8.18.0"
|
||||
c8: "npm:^11.0.0"
|
||||
eslint: "npm:^9.39.2"
|
||||
@@ -2116,10 +2116,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"undici-types@npm:~7.21.0":
|
||||
version: 7.21.0
|
||||
resolution: "undici-types@npm:7.21.0"
|
||||
checksum: 10c0/c3b4ae5f066c398acb1962505b56214ecd72843f7d7827fcc2df7a48a63d1639d3608c580ac09f836253d21fa7ba8f1a04440569ed9d332474ad01b8a010db87
|
||||
"undici-types@npm:>=7.24.0 <7.24.7":
|
||||
version: 7.24.6
|
||||
resolution: "undici-types@npm:7.24.6"
|
||||
checksum: 10c0/d9cd8befb643ac904615c280a095ba4240531f6bb4a5e75a22a7483630ca8d3f1016d2ab6ace6ceda1f63b3a2db2fe037fafe121d6917a0187573aa548ff78ca
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user