Skip to content

Add Cogladius to community skills - #75

Open
furkanyesildag wants to merge 3 commits into
stellar:mainfrom
furkanyesildag:add-cogladius-skill
Open

Add Cogladius to community skills#75
furkanyesildag wants to merge 3 commits into
stellar:mainfrom
furkanyesildag:add-cogladius-skill

Conversation

@furkanyesildag

Copy link
Copy Markdown

Adds Cogladius to ECOSYSTEM_CARDS.

Cogladius is a permissionless task marketplace on Stellar. Humans post tasks with an XLM reward locked in a non-custodial Soroban escrow; autonomous AI agents compete to solve them; a three-judge AI panel scores submissions and the escrow contract releases the XLM reward to the winner on an on-chain, ed25519-verified verdict.

The SKILL.md teaches an AI agent to register (one permissionless API call), poll open tasks, solve, and submit to earn XLM.

Cogladius is a permissionless task marketplace on Stellar: humans post tasks
with an XLM reward locked in a non-custodial Soroban escrow, AI agents compete
to solve them, and the escrow releases the reward on an on-chain ed25519-verified
judge verdict. The SKILL.md teaches an agent to register, poll tasks, solve, and
submit to earn XLM.
Copilot AI review requested due to automatic review settings July 23, 2026 18:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Cogladius to the community skills catalog.

Changes:

  • Adds a card describing Cogladius’s task marketplace workflow.
  • Links to its externally hosted SKILL.md.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread site/src/data/skills.ts
"Register your AI agent to earn XLM by completing on-chain tasks. Covers permissionless one-call registration, task polling, submitting solutions, and how the non-custodial Soroban escrow releases the XLM reward on an on-chain, ed25519-verified judge verdict.",
pathLabel: "furkanyesildag/cogladius",
copyValue:
"https://github.com/furkanyesildag/cogladius/blob/main/SKILL.md",
@furkanyesildag

Copy link
Copy Markdown
Author

Thanks for the review! I've added valid Agent Skill YAML frontmatter (name and description, plus user-invocable/argument-hint) to the upstream SKILL.md, so it's now a self-contained, installable skill:

https://github.com/furkanyesildag/cogladius/blob/main/SKILL.md

Let me know if anything else is needed 🙏

@kaankacar

Copy link
Copy Markdown
Contributor

Thanks for this — the settlement design is a genuinely nice pattern, and I checked that env.crypto().ed25519_verify matches what the docs describe, so the on-chain verdict part reads solid to me.

Before it goes on the catalog I want to make sure I'm reading a few things correctly:

Which asset does the reward actually settle in? The card and SKILL.md say XLM ("Payout: native XLM on Stellar mainnet"), but the escrow looks USDC-shaped from where I'm standing — config.usdc_sac, the token::TokenClient::new(&env, &config.usdc_sac).transfer(...) calls in post_task / release_to_winner / refund, and the README's mainnet proof txs describing "0.2 USDC locked" and "USDC paid to the winner". Is XLM a planned change that the skill is written ahead of, or am I misreading which path mainnet uses today?

The task reward field. SKILL.md documents each task as carrying reward (in XLM), but in app/app/api/agents/tasks/route.ts the response looks like rewardSol: t.rewardUsdc ?? .... If an agent reads reward, does it get a value? Wondering whether the snippet is from an earlier response shape.

Trustline. If the payout is USDC over the SAC, does the agent's G... address need a USDC trustline before it can receive anything? If so, would it be worth a line in the skill — right now an agent following it would just expect native XLM to show up.

Why the secret key? agents/cogladius-agent.js asks for STELLAR_AGENT_SECRET (an S...), but as far as I can trace it only ever calls kp.publicKey() to register — nothing seems to get signed locally. Is the secret needed for something I'm missing, or could the reference agent take just the pubkey? I ask because agents following this will end up holding a live mainnet secret in an env var, and in the no-secret branch the generated one gets printed to stdout.

Happy to merge once the asset story lines up across the card, the skill, and the contract.

furkanyesildag added a commit to furkanyesildag/cogladius that referenced this pull request Jul 31, 2026
Review feedback on stellar/stellar-dev-skill#75 surfaced that the repo
documented an asset the code no longer settles in, plus two real bugs.

Docs were pre-migration (the reward asset moved USDC -> native XLM in
0dee0b1):
- README: wrong contract ID, wrong SAC, USDC throughout; proof txs now
  labeled honestly as having run on the retired USDC deployment
- .env.local.example pointed at the RETIRED escrow contract, so anyone
  copying it wired themselves to a dead deployment
- constants.ts mainnet fallbacks still resolved to the USDC SAC/issuer
- document that usdc_sac / NEXT_PUBLIC_USDC_* / rewardUsdc are legacy
  names carrying XLM; the escrow is SEP-41 asset-agnostic

Real bugs:
- GET /api/agents/tasks returned rewardSol (a Solana-era leftover) while
  SKILL.md documented reward, so agents reading reward got undefined.
  Now returns reward + rewardXlm + rewardAsset; rewardSol deprecated.
- reference agent required STELLAR_AGENT_SECRET but never signs locally;
  it now takes STELLAR_AGENT_PUBKEY. The generate-and-print-secret path
  is gone: it could earn real mainnet XLM into an ephemeral key.
- fetchUsdcBalance looked for a non-native "XLM" trustline, so the
  dashboard showed 0 for every wallet; now reads the native balance.
  Dead addUsdcTrustline helper removed (would throw on Asset("XLM","")).
- agent and /api/agents/register validated addresses with a shape-only
  regex; a typo'd address could register and never be payable. Both now
  verify the full strkey checksum.

SKILL.md: no trustline needed (native asset), but the receiving account
must already exist and be funded to the base reserve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@furkanyesildag

Copy link
Copy Markdown
Author

Thanks for the careful read — and sorry for the runaround. You were reading the repo correctly; the repo was lying. Two of your four points were real bugs, and the other two came from documentation I'd left stale. All four are now fixed.

1. Which asset — XLM is correct, the README was stale

The skill and the card are right: mainnet settles in native XLM today. What you were reading was a pre-migration README.

The escrow is SEP-41 asset-agnostic — it moves whatever SAC address __constructor was given. The first deployment passed the Circle USDC SAC. On 2026-07-11 I redeployed against the native XLM SAC, specifically so that agents wouldn't need a trustline (more on that in #3):

Live Retired
Escrow CAC5EDF76M5LY43BNHT47Y5NZRHO4ZRH7SRFPNHATGNKN2DI3SNK75PL CBZ54RRGG4S5RZXW2BC26SIDFYVTB5XQDV3AYU42WQYBRNNUWZBACYTO
Reward SAC CAS3J7GY…OWMA (native XLM) CCW67TSZ…MI75 (USDC)

The config.usdc_sac field and the token::TokenClient::new(&env, &config.usdc_sac).transfer(...) calls you traced are real — but usdc_sac is a legacy field name, not the asset. I kept the name because renaming it changes the contract spec and would force a redeploy of a live contract for a cosmetic gain. It's now documented as such in the contract source, and the same note covers the NEXT_PUBLIC_USDC_* env vars and the rewardUsdc identifier. Anywhere the name says USDC, the value is XLM.

What I fixed: the README was entirely pre-migration — wrong contract ID, wrong SAC, USDC throughout. Swept. app/.env.local.example was worse: it pointed at the retired contract, so anyone copying it would have wired themselves to a dead deployment. Also fixed, along with the mainnet fallbacks in app/lib/constants.ts.

One thing I want to flag rather than paper over: the three proof transactions in the README are genuine mainnet transactions, but they ran against the USDC deployment. Same contract code, byte-for-byte — only the SAC address in the constructor differs — but they are not XLM proofs. I've labeled them honestly instead of quietly relabeling them "XLM". If you'd like a fresh post → release → refund cycle on CAC5EDF7… before this goes on the catalog, say the word and I'll run one and post the hashes.

2. The reward field — you found a real bug

Correct, and thank you. An agent reading reward got undefined. rewardSol is a leftover from the Solana original that survived two asset migrations; SKILL.md documented the field the response should have had.

The endpoint now returns reward (the documented name), plus rewardXlm as an explicit alias and rewardAsset: "XLM". rewardSol stays for one release so existing agents don't break, marked deprecated in both the code and the skill.

3. Trustline — not needed, and that's exactly why XLM

This one resolves itself once #1 is straight: the payout is the native asset, so no trustline is involved. Avoiding the trustline onboarding step is precisely why I switched off USDC — an agent shouldn't have to sign a changeTrust before it can earn.

There is a real precondition, though, and you were right that the skill was silent on it. Native or not, the receiving account has to already exist on-chain (funded to at least the 1 XLM base reserve). A freshly generated, never-funded address cannot receive the payout. That's now stated in SKILL.md.

While checking this I found the migration had left two broken paths behind: fetchUsdcBalance was still looking for a non-native balance line with code XLM and a USDC issuer — so the dashboard showed 0 for every connected wallet — and a dead addUsdcTrustline helper that would have thrown on new Asset("XLM", ""). Fixed and removed respectively.

4. The secret key — you're right, it never needed one

Nothing is signed locally. Registration is authenticated by the API key it returns, and payouts are pushed to the address by the contract; kp.publicKey() was genuinely the only use. Asking for an S... bought the agent no capability and created exactly the exposure you describe — worse in the no-secret branch, which generated a keypair and printed the secret to stdout, so a copy-pasted quickstart could have earned real mainnet XLM into a key living only in a terminal scrollback.

The reference agent now takes STELLAR_AGENT_PUBKEY (public key only). STELLAR_AGENT_SECRET is still accepted for compatibility but only its public key is derived, with a deprecation warning. The generate-and-print path is gone: with no address configured the agent refuses to start and tells you to generate one you control, rather than inventing an address on your behalf.

One more thing came out of testing that fix. Both the agent and POST /api/agents/register validated addresses with a bare ^G[A-Z2-7]{55}$ shape regex, which accepts a typo'd address that isn't a valid strkey — an agent could register an address it can never be paid at. Both sides now validate the full strkey checksum (StrKey.isValidEd25519PublicKey / Keypair.fromPublicKey).


Changed: README.md, SKILL.md, agents/cogladius-agent.js, app/.env.local.example, app/lib/constants.ts, app/lib/stellar.ts, app/lib/sorobanEscrow.ts, app/app/api/agents/tasks/route.ts, app/app/api/agents/register/route.ts, contracts/cogladius-escrow/src/lib.rs.

Contract suite is green (16 tests, including bad-signature revert and verdict-key rotation) and the app typechecks clean. Happy to run the fresh XLM lifecycle on the live contract if you want that in the README before merge.


@Copilot — on the frontmatter: SKILL.md does carry Agent Skill YAML frontmatter (name: cogladius, description, plus user-invocable and argument-hint) and has since b1325cf, which landed before this catalog PR was opened. The linked file on main starts with the --- block, not the H1 — I think the review looked at an earlier state of the file. Worth a re-check against current main.

@kaankacar

Copy link
Copy Markdown
Contributor

@furkanyesildag — thanks for the thorough turnaround back in July. I went through everything again this week, and your fixes hold up under testing: get_config on the live escrow returns usdc_sac = CAS3J7GY…XOWMA, which I independently derived as the pubnet native XLM SAC, so the legacy-field-name explanation checks out exactly as you described it. The reward / rewardXlm / rewardAsset fields are all there now, and the reference agent takes a pubkey and refuses to invent an address rather than printing a generated secret. That's a clean set of fixes, and I appreciate you finding the strkey-checksum bug on your own while you were in there.

I also pushed two small commits to your branch: a merge of main to clear a conflict (#82 landed a card in the same spot), and a slightly shorter card description so it sits better next to its neighbours. Neither touches the substance.

I ran the skill end to end, the way an agent following it would, and three things came up before I can merge.

1. The payout path has never run on the live contract

I'd like to take you up on the offer you made at the end of your last comment.

The escrow holds 3 XLM, and its balance hasn't changed since the task was funded on 2026-08-06 at 16:29 UTC — funds went in, nothing has come out. As far as I can trace, release_to_winner has never executed on CAC5EDF7…. Put that together with the README proofs running against the retired USDC deployment — which you flagged honestly rather than relabelling, and I do appreciate that — and the XLM settlement path is currently undemonstrated on mainnet.

So yes please: a fresh post → release → refund cycle on the live contract, with the hashes posted here, would close this out. It's the single biggest thing standing between this PR and a merge.

2. The API accepts submissions on expired tasks

Task 1's deadline passed on 2026-08-06 (timeRemainingSeconds: 0), and on 2026-08-10 POST /api/agents/submit accepted my submission anyway and ran the full three-judge panel on it. The contract enforces the deadline, but the API doesn't gate on it — so every late submission burns a real judging run for a reward that can't be released. Rejecting past the deadline, before it reaches the judges, would save you the cost.

3. There's no duplicate-submission guard

I submitted to the same task twice with the same API key. Both went through and were independently judged, even though /api/agents/tasks already exposes alreadySubmitted. Same cost concern as above — an agent sitting in a poll loop could run that up quickly.

What did work

Everything else I could exercise behaved as documented. Registration is idempotent and returns the same key. The auth check rejects a bad key, and unknown task IDs are rejected properly. The judge panel is genuinely live — it came back with real per-judge reasoning and correctly scored my placeholder text a zero. The architecture does what you say it does; it just hasn't paid anyone yet.

One apology: proving this out meant writing to your production marketplace. I left two submissions on the expired task 1, both labelled as catalog review probes — please purge them, and sorry for the noise.

To merge

  • The fresh XLM lifecycle on CAC5EDF7…, with hashes
  • A deadline check on submit
  • A duplicate-submission guard

And whenever you have a live task or two on the board, that would help — right now the only task is expired, so an agent installing this from the catalog has nothing to earn yet.

Ping me when those land and I'll re-review straight away.

furkanyesildag added a commit to furkanyesildag/cogladius that referenced this pull request Aug 10, 2026
Catalog review on stellar/stellar-dev-skill#75 found the API accepted
submissions the escrow can never pay out, each one burning a real
three-judge run.

- Deadline gate: the contract refuses to release past `deadline`, so a
  late submission cost three LLM calls for a reward that cannot settle.
  Now rejected with 409 `deadline_passed` before the panel runs.
- Duplicate guard: addSubmission already deduped by agent, so storage
  stayed correct, but the panel re-ran on every resubmit and appended a
  fresh set of verdicts. A polling agent could run that up quickly.
  Now rejected with 409 `already_submitted`, matching the
  `alreadySubmitted` flag /api/agents/tasks already advertises.

Error responses carry a machine-readable `code` so agents can branch
without parsing prose.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@furkanyesildag

Copy link
Copy Markdown
Author

Thanks for going this deep, and for testing the payout path rather than taking the README's word for it. Your read was right on every count: the escrow had been sitting on 3 XLM untouched since 2026-08-06, and release_to_winner had never executed on CAC5EDF7…K75PL. The settlement path was documented, unit-tested and deployed, but never actually exercised with real XLM. That is now fixed, along with both API issues.

1. Fresh XLM lifecycle on the live contract

Ran end to end on CAC5EDF76M5LY43BNHT47Y5NZRHO4ZRH7SRFPNHATGNKN2DI3SNK75PL with real XLM:

Step Transaction What it shows
post_task faef88b8…33c87c 1 XLM locked from the poster into the escrow via the native SAC
release_to_winner 7a67b7e5…16c6a7 Verdict signature verified on-chain, exactly 1 XLM paid to the winner
post_task 26f583b3…a315ac Second task funded, to exercise the cancel path
refund 6721bd79…23cc76 Reward returned to the poster on a poster-authorized cancel

Before the valid verdict was accepted, I sent the same release_to_winner call with a forged 64-byte signature. The contract refused it with Error(Crypto, InvalidInput) out of env.crypto().ed25519_verify. It fails during simulation, so it never reaches the ledger and has no hash to link, which is worth stating plainly rather than implying there is a failed transaction to inspect.

Balances, if you want to check independently: the winner account went from 9.3649331 to 10.3649331 XLM, so exactly the reward and no trustline anywhere in the path. The escrow's balance reads 3 XLM again because the refund leg returned the second task's reward, but its updated timestamp moved from 1786033750 to 1786394799, which is the movement you were looking for.

The runner is committed as scripts/mainnet-lifecycle.js so this is repeatable instead of a one-off I did by hand. It reads keys from app/.env.local and never prints them. README now leads with these four transactions; the old USDC ones are collapsed into a <details> block and labeled as historic rather than deleted.

2. Deadline check on submit

Landed. The gate runs before the judge panel, so a late submission costs nothing:

409  { "code": "deadline_passed", "deadline": …, "deadlineIso": "…" }

You were right about why this matters: the contract will not release past the deadline, so every late submission was three real LLM calls spent on a reward that could never settle.

3. Duplicate submission guard

Landed, same shape:

409  { "code": "already_submitted" }

One detail from your probe that is worth reporting back, because it was slightly worse than "both went through". addSubmission already deduplicated by agent, so storage stayed correct and your two submissions collapsed into one row. The judge panel, though, ran on both and appended a fresh set of verdicts each time. Task 1 was holding 1 submission and 6 verdicts. So the storage layer looked fine while the cost and the verdict history did not, which is exactly the failure mode you flagged. The guard now sits at the route, before the panel, and matches the alreadySubmitted flag /api/agents/tasks already advertises.

Both error responses carry a machine-readable code so an agent can branch without parsing Turkish prose.

4. Your probes

Purged. Task 1 is back to 0 submissions and 0 verdicts, and its status is reset from AwaitingDecision to Open. No apology needed, incidentally: writing to production is the only way you could have found either of these, and finding them was worth more than the noise cost.

I also cleaned up a junk registration of my own from when I was testing the pubkey change back in July.

Still open

Live tasks on the board. This is the one item from your list I have not done yet. Task 1 is expired and it is currently the only one, so an agent installing from the catalog still has nothing to earn. I would rather post a couple of genuinely useful tasks with real rewards than seed filler to clear a checkbox, so give me a little time on that one. Say the word if you would prefer it done before merge rather than alongside it, and I will prioritise it.

Everything else above is on main now. Ping me when you have re-reviewed and I will turn around anything else quickly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants