Skip to content

Demand metrics

Demand metrics #750

name: Demand metrics
on:
workflow_dispatch:
schedule:
- cron: "17 */4 * * *"
issues:
types: [opened, edited, reopened, labeled, unlabeled, closed]
release:
types: [published, edited, deleted]
concurrency:
group: demand-metrics
cancel-in-progress: false
permissions:
contents: write
issues: read
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: main
- uses: actions/github-script@v7
with:
script: |
const fs = require("fs");
const owner = context.repo.owner;
const repo = context.repo.repo;
const price = 29;
const teamPrice = 203;
const target = 200;
const allIssues = await github.paginate(github.rest.issues.listForRepo, {
owner,
repo,
state: "all",
per_page: 100
});
const plainIssues = allIssues.filter((issue) => !issue.pull_request);
const issueText = (issue) => `${issue.title || ""}\n${issue.body || ""}`;
const hasLabel = (issue, label) => (issue.labels || []).some((item) => item.name === label);
const isNotBuyerIntent = (issue) => {
const text = issueText(issue);
return hasLabel(issue, "not-buyer-intent") ||
text.includes("agent-ops-discussion-order-bridge-v1") ||
text.includes("automation-generated bridge from owner-authored discussion");
};
const isOrderIntent = (issue) => {
const text = issueText(issue);
return hasLabel(issue, "order-request") ||
hasLabel(issue, "template-pack-request") ||
hasLabel(issue, "budget-request") ||
hasLabel(issue, "invoice-request") ||
hasLabel(issue, "expense-report") ||
hasLabel(issue, "quote-request") ||
hasLabel(issue, "purchase-order") ||
hasLabel(issue, "vendor-onboarding") ||
hasLabel(issue, "security-review") ||
hasLabel(issue, "delivery-acceptance") ||
hasLabel(issue, "payment-ready") ||
text.includes("Order request: Agent Ops Command Center") ||
text.includes("Template pack request: Agent Ops Command Center") ||
text.includes("Budget request: Agent Ops Command Center") ||
text.includes("Invoice request: Agent Ops Command Center") ||
text.includes("Expense report request: Agent Ops Command Center") ||
text.includes("Quote request: Agent Ops Command Center") ||
text.includes("Purchase order request: Agent Ops Command Center") ||
text.includes("Vendor onboarding request: Agent Ops Command Center") ||
text.includes("Security review request: Agent Ops Command Center") ||
text.includes("Delivery acceptance request: Agent Ops Command Center") ||
text.includes("Payment-ready request: Agent Ops Command Center") ||
text.includes("Primary team request: Agent Ops Command Center") ||
text.includes("Fast team checkout request: Agent Ops Command Center") ||
text.includes("Invoice request packet: Agent Ops Command Center") ||
text.includes("Request package: Team license - 7 seats - $203 gross") ||
text.includes("Request package: Individual seat - $29");
};
const issues = plainIssues.filter((issue) => !isNotBuyerIntent(issue) && isOrderIntent(issue));
const unlabeledOrders = issues.filter((issue) => !hasLabel(issue, "order-request")).length;
const revenueProofIssues = await github.paginate(github.rest.issues.listForRepo, {
owner,
repo,
state: "all",
labels: "revenue-proof",
per_page: 100
});
const releases = await github.rest.repos.listReleases({
owner,
repo,
per_page: 20
});
const releaseAssets = releases.data.flatMap((release) => release.assets || []);
const previewDownloads = releaseAssets
.filter((asset) => asset.name.includes("preview"))
.reduce((sum, asset) => sum + asset.download_count, 0);
const releaseTarballDownloads = releaseAssets
.filter((asset) => asset.name.endsWith(".tgz"))
.reduce((sum, asset) => sum + asset.download_count, 0);
const totalReleaseDownloads = releaseAssets
.reduce((sum, asset) => sum + asset.download_count, 0);
const repoInfo = await github.rest.repos.get({ owner, repo });
const repositoryStars = repoInfo.data.stargazers_count || 0;
const repositoryForks = repoInfo.data.forks_count || 0;
const repositoryWatchers = repoInfo.data.subscribers_count || 0;
const orders = issues.length;
const teamOrders = issues.filter((issue) => {
const body = issue.body || "";
return body.includes("Team license - 7 seats - $203 gross") ||
body.includes("I am ready to pay $203 for the 7-seat team license when checkout is ready");
}).length;
const previewOnlyOrders = issues.filter((issue) => {
const body = issue.body || "";
return body.includes("Evaluating preview only") ||
body.includes("I want to evaluate the preview first") ||
body.includes("I only want updates for now");
}).length;
const individualOrders = Math.max(0, orders - teamOrders - previewOnlyOrders);
const possibleGross = (teamOrders * teamPrice) + (individualOrders * price);
const parseGrossUsd = (body) => {
const match = (body || "").match(/### Gross revenue proven USD\s+([0-9]+(?:\.[0-9]{1,2})?)/i);
if (!match) return 0;
const amount = Number.parseFloat(match[1]);
return Number.isFinite(amount) && amount > 0 ? amount : 0;
};
const confirmedRevenueProofs = revenueProofIssues
.map((issue) => ({ issue, amount: parseGrossUsd(issue.body || "") }))
.filter(({ issue, amount }) => amount > 0 &&
hasLabel(issue, "revenue-confirmed") &&
(issue.body || "").includes("I confirm this issue records real checkout, receipt, payout, or seller-dashboard evidence."));
const grossRevenue = confirmedRevenueProofs.reduce((sum, proof) => sum + proof.amount, 0);
const status = grossRevenue >= target
? "revenue target reached"
: grossRevenue > 0
? "partial paid revenue proven"
: orders > 0
? "orders pending checkout"
: "no paid checkout";
const metrics = {
orders,
individual_order_intents: individualOrders,
team_license_order_intents: teamOrders,
preview_only_intents: previewOnlyOrders,
unlabeled_order_intents: unlabeledOrders,
revenue_proof_issues: revenueProofIssues.length,
confirmed_revenue_proof_issues: confirmedRevenueProofs.length,
preview_downloads: previewDownloads,
release_tarball_downloads: releaseTarballDownloads,
total_release_downloads: totalReleaseDownloads,
repository_stars: repositoryStars,
repository_forks: repositoryForks,
repository_watchers: repositoryWatchers,
possible_gross_if_all_orders_paid_usd: possibleGross,
gross_revenue_usd: Number(grossRevenue.toFixed(2)),
target_revenue_usd: target,
price_usd: price,
team_license_price_usd: teamPrice,
sales_needed: Math.ceil(target / price),
team_licenses_needed: Math.ceil(target / teamPrice),
checkout_live: false,
revenue_proof_required: "checkout, receipt, payout, or seller-dashboard evidence",
revenue_counting_rule: "gross revenue counts only from revenue-proof issues that include a positive amount, the required confirmation text, and the revenue-confirmed label",
status,
updated_at: new Date().toISOString()
};
fs.mkdirSync("metrics", { recursive: true });
fs.writeFileSync("metrics/status.json", `${JSON.stringify(metrics, null, 2)}\n`);
const readme = [
"# Demand Metrics",
"",
"Current status:",
"",
`- Orders: \`${metrics.orders}\``,
`- Individual order intents: \`${metrics.individual_order_intents}\``,
`- Team license order intents: \`${metrics.team_license_order_intents}\``,
`- Preview-only intents: \`${metrics.preview_only_intents}\``,
`- Unlabeled order intents: \`${metrics.unlabeled_order_intents}\``,
`- Revenue proof issues: \`${metrics.revenue_proof_issues}\``,
`- Confirmed revenue proof issues: \`${metrics.confirmed_revenue_proof_issues}\``,
`- Preview downloads: \`${metrics.preview_downloads}\``,
`- Release tarball downloads: \`${metrics.release_tarball_downloads}\``,
`- Total release downloads: \`${metrics.total_release_downloads}\``,
`- Repository stars: \`${metrics.repository_stars}\``,
`- Repository forks: \`${metrics.repository_forks}\``,
`- Repository watchers: \`${metrics.repository_watchers}\``,
`- Possible gross if all order requests pay: \`$${metrics.possible_gross_if_all_orders_paid_usd}\``,
`- Gross revenue proven: \`$${metrics.gross_revenue_usd}\``,
`- Target revenue: \`$${metrics.target_revenue_usd}\``,
`- Price target: \`$${metrics.price_usd}\``,
`- Team license target: \`$${metrics.team_license_price_usd}\``,
`- Sales needed: \`${metrics.sales_needed}\``,
`- Team licenses needed: \`${metrics.team_licenses_needed}\``,
`- Checkout live: \`${metrics.checkout_live}\``,
`- Revenue proof required: \`${metrics.revenue_proof_required}\``,
`- Revenue counting rule: \`${metrics.revenue_counting_rule}\``,
`- Status: \`${metrics.status}\``,
`- Updated: \`${metrics.updated_at}\``,
`- Auto-refresh cadence: \`every 4 hours, plus issue/release/workflow events\``,
"",
"This repo cannot prove paid revenue until an authenticated checkout or seller dashboard exists.",
""
].join("\n");
fs.writeFileSync("metrics/README.md", readme);
- name: Commit metrics
run: |
if git diff --quiet -- metrics/status.json metrics/README.md; then
echo "No metrics changes"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add metrics/status.json metrics/README.md
git commit -m "Update demand metrics"
git pull --rebase origin main
git push origin HEAD:main