Skip to content

CLI/MCP: Listing Readiness sözleşmesini konuş - #133

Open
semihbugrasezer wants to merge 1 commit into
mainfrom
feat/listing-readiness-contract
Open

semihbugrasezer wants to merge 1 commit into
mainfrom
feat/listing-readiness-contract

Conversation

@semihbugrasezer

@semihbugrasezer semihbugrasezer commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Ne değişti

  • analyze / optimize output şemaları artık scoringVersion, scoreType, overallScore, evaluatedAt, evidence, limitations zorunlu tutuyor; seoScore sadece deprecated uyumluluk alias'ı.
  • Weak point'ler ruleId, explanation ve source istiyor; formatter her bulguyu reason → fix olarak, Etsy kaynağına link vererek basıyor.
  • Skor satırı: **Listing Readiness: N/100** · rubric <version>.
  • README ve skills/seerxo-etsy-seo/SKILL.md skorun ne olduğunu ve ne olmadığını söylüyor.

Doğrulama

  • npm test → 104/104 pass
  • npm run lint (node --check zinciri) → temiz
  • npm run build → package validation passed

Bağımlılık

semihbugrasezer/seerxo-backend#105 önce merge olmalı — bu şema onun çıktısını doğruluyor (scoringVersion const olarak etsy-guidance-2026-04-27'e pinli).


Summary by cubic

Switches CLI/MCP analyze and optimize outputs from a legacy SEO score to a versioned Listing Readiness contract with sourced findings and explicit limitations. seoScore stays as a deprecated compatibility alias; no behavior changes for consumers that ignored it.

  • Output schemas now require scoringVersion, scoreType, overallScore, evaluatedAt, evidence, and limitations.
  • Weak points now require ruleId, explanation, and source; the formatter renders each finding as reason → fix with a link to its Etsy source.
  • The score line now reads Listing Readiness: N/100 · rubric <version>, and README and skill docs state what the score does and does not predict.
  • optimize now rejects rewrites that lower the deterministic readiness score.
  • Depends on semihbugrasezer/seerxo-backend#105, which must merge before this schema's pinned scoringVersion output is validated.

Written for commit ef478ec. Summary will update on new commits.

Review in cubic

- analyze/optimize output schemas now require scoringVersion, scoreType,
  overallScore, evaluatedAt, evidence and limitations; seoScore is marked
  deprecated and kept only as a compatibility alias
- weak points require ruleId, explanation and a source, and the formatter
  renders each finding as "reason -> fix" with a link to its Etsy source
- score line reads "Listing Readiness: N/100 - rubric <version>"
- README and the seerxo-etsy-seo skill say what the score is and is not

Verified: 104/104 tests, node --check clean, package validation passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017onzZmmB9gTutoAaCgPz9u
assert.ok(text.includes('Listing Readiness'));
assert.ok(!text.includes('SEO Score'));
assert.ok(text.includes('etsy-guidance-2026-04-27'));
assert.ok(text.includes('https://www.etsy.com/seller-handbook/article/382774281517'));

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

3 issues found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/mcp/tools.js">

<violation number="1" location="src/mcp/tools.js:89">
P1: When the backend returns the canonical readiness fields without the legacy alias, MCP clients reject the response because `seoScore` is still required. Remove `seoScore` from `required`; `deprecated` does not make a required property optional.</violation>
</file>

<file name="src/formatters/listing.js">

<violation number="1" location="src/formatters/listing.js:17">
P3: When neither `overallScore` nor `seoScore` is present, `score` falls back to the string `'n/a'`, but the line unconditionally appends `/100`, so it renders `Listing Readiness: n/a/100`. Only append `/100` when the score is numeric.</violation>
</file>

<file name="tests/listing-tools.test.js">

<violation number="1" location="tests/listing-tools.test.js:188">
P2: Assert the complete rendered `[source](...)` fragment instead of searching for the URL as an arbitrary substring. This assertion can pass when the Etsy URL appears inside another URL or unrelated text without verifying the link target.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/mcp/tools.js
},
},
required: ['seoScore', 'subScores', 'weakPoints', 'missingKeywords', 'tagUtilization'],
required: ['scoringVersion', 'scoreType', 'overallScore', 'evaluatedAt', 'evidence', 'limitations', 'seoScore', 'subScores', 'weakPoints', 'missingKeywords', 'tagUtilization'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: When the backend returns the canonical readiness fields without the legacy alias, MCP clients reject the response because seoScore is still required. Remove seoScore from required; deprecated does not make a required property optional.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/mcp/tools.js, line 89:

<comment>When the backend returns the canonical readiness fields without the legacy alias, MCP clients reject the response because `seoScore` is still required. Remove `seoScore` from `required`; `deprecated` does not make a required property optional.</comment>

<file context>
@@ -66,15 +86,15 @@ const AUDIT_OUTPUT_SCHEMA = {
     },
   },
-  required: ['seoScore', 'subScores', 'weakPoints', 'missingKeywords', 'tagUtilization'],
+  required: ['scoringVersion', 'scoreType', 'overallScore', 'evaluatedAt', 'evidence', 'limitations', 'seoScore', 'subScores', 'weakPoints', 'missingKeywords', 'tagUtilization'],
 };
 
</file context>
Suggested change
required: ['scoringVersion', 'scoreType', 'overallScore', 'evaluatedAt', 'evidence', 'limitations', 'seoScore', 'subScores', 'weakPoints', 'missingKeywords', 'tagUtilization'],
required: ['scoringVersion', 'scoreType', 'overallScore', 'evaluatedAt', 'evidence', 'limitations', 'subScores', 'weakPoints', 'missingKeywords', 'tagUtilization'],

assert.ok(text.includes('Listing Readiness'));
assert.ok(!text.includes('SEO Score'));
assert.ok(text.includes('etsy-guidance-2026-04-27'));
assert.ok(text.includes('https://www.etsy.com/seller-handbook/article/382774281517'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Assert the complete rendered [source](...) fragment instead of searching for the URL as an arbitrary substring. This assertion can pass when the Etsy URL appears inside another URL or unrelated text without verifying the link target.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/listing-tools.test.js, line 188:

<comment>Assert the complete rendered `[source](...)` fragment instead of searching for the URL as an arbitrary substring. This assertion can pass when the Etsy URL appears inside another URL or unrelated text without verifying the link target.</comment>

<file context>
@@ -164,6 +182,11 @@ describe('formatters', () => {
+    assert.ok(text.includes('Listing Readiness'));
+    assert.ok(!text.includes('SEO Score'));
+    assert.ok(text.includes('etsy-guidance-2026-04-27'));
+    assert.ok(text.includes('https://www.etsy.com/seller-handbook/article/382774281517'));
+    assert.ok(text.includes('does not predict search rank'));
     assert.ok(text.includes('[high] (tags) Exactly 13 tags — fix: Use all 13 tag slots.'));
</file context>
Suggested change
assert.ok(text.includes('https://www.etsy.com/seller-handbook/article/382774281517'));
assert.ok(text.includes('[source](https://www.etsy.com/seller-handbook/article/382774281517)'));

Comment thread src/formatters/listing.js
`# Listing Audit\n\n` +
`**SEO Score: ${data.seoScore}/100** — title ${s.title}, tags ${s.tags}, description ${s.description}, completeness ${s.completeness}\n\n` +
`# Listing Readiness\n\n` +
`**Listing Readiness: ${score}/100**${data.scoringVersion ? ` · rubric ${data.scoringVersion}` : ''} — title ${s.title}, tags ${s.tags}, description ${s.description}, completeness ${s.completeness}\n\n` +

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: When neither overallScore nor seoScore is present, score falls back to the string 'n/a', but the line unconditionally appends /100, so it renders Listing Readiness: n/a/100. Only append /100 when the score is numeric.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/formatters/listing.js, line 17:

<comment>When neither `overallScore` nor `seoScore` is present, `score` falls back to the string `'n/a'`, but the line unconditionally appends `/100`, so it renders `Listing Readiness: n/a/100`. Only append `/100` when the score is numeric.</comment>

<file context>
@@ -1,21 +1,24 @@
-    `# Listing Audit\n\n` +
-    `**SEO Score: ${data.seoScore}/100** — title ${s.title}, tags ${s.tags}, description ${s.description}, completeness ${s.completeness}\n\n` +
+    `# Listing Readiness\n\n` +
+    `**Listing Readiness: ${score}/100**${data.scoringVersion ? ` · rubric ${data.scoringVersion}` : ''} — title ${s.title}, tags ${s.tags}, description ${s.description}, completeness ${s.completeness}\n\n` +
     `## Weak points\n${weakPoints || 'None — this listing passes every check.'}\n\n` +
     `## Missing keywords\n${(data.missingKeywords || []).join(', ') || 'none'}\n\n` +
</file context>
Suggested change
`**Listing Readiness: ${score}/100**${data.scoringVersion ? ` · rubric ${data.scoringVersion}` : ''} — title ${s.title}, tags ${s.tags}, description ${s.description}, completeness ${s.completeness}\n\n` +
`**Listing Readiness: ${typeof score === 'number' ? `${score}/100` : score}**${data.scoringVersion ? ` · rubric ${data.scoringVersion}` : ''} — title ${s.title}, tags ${s.tags}, description ${s.description}, completeness ${s.completeness}\n\n` +

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.

2 participants