Skip to content

fix: encode honors context-vs-input governance distinction#98

Merged
klappy merged 3 commits intomainfrom
fix/encode-context-handling
Apr 16, 2026
Merged

fix: encode honors context-vs-input governance distinction#98
klappy merged 3 commits intomainfrom
fix/encode-context-handling

Conversation

@klappy
Copy link
Copy Markdown
Owner

@klappy klappy commented Apr 16, 2026

Closes the gap between governance and code surfaced during PR #96 testing.

The Gap

Governance (klappy://odd/encoding-types/how-to-write-encoding-types#context-vs-input):

input generates artifacts; context only informs quality scoring

Server code (after PR #96):

const fullInput = context ? `${input}\n${context}` : input;
const artifacts = parseStructuredInput(fullInput, types); // wrong

Result: context paragraphs became separate standalone artifacts instead of informing the primary artifact's score.

The Fix

  • runEncodeAction: parsers receive input only (not fullInput)
  • runEncodeAction: scoring receives input + context per artifact
  • scoreArtifactQuality: accepts optional scoringText parameter
  • Inline comments cite the governance doc to prevent regression

Verification

TypeScript compiles clean. Manual verification on preview after merge.


Note

Medium Risk
Behavior of encode output and quality ratings changes, which could affect downstream expectations/tests, but the change is localized and not security-sensitive.

Overview
Fixes encode to honor the governance rule that context should not generate artifacts.

runEncodeAction now parses artifacts from input only, and passes context only into quality scoring by appending it to each artifact’s body at scoring time. scoreArtifactQuality is updated to accept optional scoringText, so length/rationale/concreteness checks can consider body + context without changing the persisted artifact content.

Reviewed by Cursor Bugbot for commit e95af36. Bugbot is set up for automated code reviews on this repo. Configure here.

Server now follows klappy://odd/encoding-types/how-to-write-encoding-types
section 'Context vs Input': input generates artifacts; context only
informs quality scoring.

Before this change, fullInput (input + context) was passed to both
the parser and the scorer, causing context paragraphs to become
separate standalone artifacts. The governance says context is
metadata, not content.

Changes:
- runEncodeAction: parsers receive input only (not fullInput)
- runEncodeAction: scoring receives input + context per artifact so
  background information still counts toward quality
- scoreArtifactQuality: accepts optional scoringText parameter that
  defaults to artifact.body when not provided
- Inline comments cite the governance doc to prevent regression

Closes the gap between governance and code surfaced during PR #96
testing.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 16, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
oddkit e95af36 Commit Preview URL

Branch Preview URL
Apr 16 2026, 03:11 AM

Comment thread workers/src/orchestrate.ts Outdated
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Non-empty check passes for empty artifacts with context
    • Changed the non-empty criterion check on line 428 from text.length > 0 back to artifact.body.length > 0 so that context cannot substitute for actual artifact content.
Preview (e95af368db)
diff --git a/workers/src/orchestrate.ts b/workers/src/orchestrate.ts
--- a/workers/src/orchestrate.ts
+++ b/workers/src/orchestrate.ts
@@ -402,15 +402,22 @@
 function scoreArtifactQuality(
   artifact: ParsedArtifact,
   criteria: Array<{ criterion: string; check: string; gapMessage: string }>,
+  scoringText?: string,
 ): { score: number; maxScore: number; level: string; gaps: string[]; suggestions: string[] } {
   const gaps: string[] = [];
   const suggestions: string[] = [];
   let score = 0;
+  // Governance: context informs quality scoring. When scoringText is provided
+  // (artifact.body + context), criteria check against that combined text so
+  // background information in context (rationale, alternatives, evidence)
+  // counts toward the artifact's quality without becoming separate artifacts.
+  // See: klappy://odd/encoding-types/how-to-write-encoding-types#context-vs-input
+  const text = scoringText ?? artifact.body;
 
   if (criteria.length === 0) {
-    if (artifact.body.split(/\s+/).length >= 10) score++;
+    if (text.split(/\s+/).length >= 10) score++;
     else suggestions.push("Expand — more detail improves quality");
-    if (/because|due to|since/i.test(artifact.body)) score++;
+    if (/because|due to|since/i.test(text)) score++;
     else suggestions.push("Add rationale");
     return { score, maxScore: 2, level: score >= 2 ? "adequate" : "weak", gaps, suggestions };
   }
@@ -419,11 +426,11 @@
     const ck = c.check.toLowerCase();
     let passed = false;
     if (ck.includes("non-empty")) passed = artifact.fields.length > 3 || artifact.body.length > 0;
-    else if (ck.includes("10")) passed = artifact.body.split(/\s+/).length >= 10;
-    else if (ck.includes("number") || ck.includes("concrete")) passed = /\d/.test(artifact.body);
+    else if (ck.includes("10")) passed = text.split(/\s+/).length >= 10;
+    else if (ck.includes("number") || ck.includes("concrete")) passed = /\d/.test(text);
     else if (ck.includes("interpretation") || ck.includes("does not contain")) passed = !/should|better|worse|means|implies/i.test(artifact.body);
     else if (ck.includes("prohibition") || ck.includes("requirement")) passed = /must|must not|never|always|shall/i.test(artifact.body);
-    else passed = artifact.body.split(/\s+/).length >= 5;
+    else passed = text.split(/\s+/).length >= 5;
     if (passed) score++;
     else { gaps.push(c.gapMessage); suggestions.push(c.gapMessage); }
   }
@@ -1415,19 +1422,26 @@
   state?: OddkitState,
 ): Promise<ActionResult> {
   const startMs = Date.now();
-  const fullInput = context ? `${input}\n${context}` : input;
+  // Governance: input generates artifacts; context only informs quality scoring.
+  // See: klappy://odd/encoding-types/how-to-write-encoding-types#context-vs-input
+  // Do not pass fullInput to parsers — that would create separate artifacts
+  // for each context paragraph instead of letting context inform scoring.
 
   const types = await discoverEncodingTypes(fetcher, canonUrl);
-  const structured = isStructuredInput(fullInput);
+  const structured = isStructuredInput(input);
   const artifacts = structured
-    ? parseStructuredInput(fullInput, types)
-    : parseUnstructuredInput(fullInput, types);
+    ? parseStructuredInput(input, types)
+    : parseUnstructuredInput(input, types);
 
-  // Score each artifact using its type's quality criteria
+  // Score each artifact using its type's quality criteria.
+  // When context is provided, append it to the artifact's body for scoring
+  // so background information (rationale, alternatives, evidence) counts
+  // toward the artifact's quality without becoming separate artifacts.
   const scoredArtifacts = artifacts.map((a) => {
     const typeDef = types.find((t) => t.letter === a.type);
     const criteria = typeDef ? typeDef.qualityCriteria : [];
-    const quality = scoreArtifactQuality(a, criteria);
+    const scoringText = context ? `${a.body}\n${context}` : undefined;
+    const quality = scoreArtifactQuality(a, criteria, scoringText);
     return { title: a.title, type: a.type, typeName: a.typeName, content: a.body, fields: a.fields, quality };
   });

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit e6e9e6c. Configure here.

Comment thread workers/src/orchestrate.ts Outdated
The non-empty check in scoreArtifactQuality was using text (which
includes appended context) instead of artifact.body. This meant an
artifact with an empty body would pass the non-empty check whenever
context was supplied, defeating the purpose of the validity check.

Restored the check to use artifact.body.length so context informs
quality scoring but cannot substitute for actual artifact content.
@klappy klappy merged commit 760f613 into main Apr 16, 2026
7 of 8 checks passed
@klappy klappy deleted the fix/encode-context-handling branch April 16, 2026 03:19
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