fix(effort-graph): clarify Citation and Blob behavior - #226
Conversation
Address review blockers and follow-ups on the Citation/Blob stack: - Skill hero teaches 8 collections / 15 mutations + cite create-order - Supersede mutation-enum Constraint to fifteen (WriteCitation/WriteBlob) - Roll up Blob naming Decision and Issue to cites→Citation→optional Blob - Enforce same-effort for cites and Citation.blob; drop CreateEffort.cites - Fix relations() Effort membership; default records omit blob - Teach cite workflow in effort-modeling / grill-with-efforts - Add planner, writer, CLI, and live GraphQL cite/blob coverage Change-Id: I11e65dc7c1390e1c2d3b6c946ebdecffe3d16605
|
Queued — the merge queue status continues in this comment ↓. |
Rewrite the Citation and Blob documentation in plain language. Explain the record types, source workflow, and links directly instead of relying on internal terms such as epistemic records, homogeneous refs, or retro-linking. Keep installed skill copies and the dogfooded Decision and Issue aligned with the source skills. Change-Id: If2114f6509df1a0222014d9690e039faefcc86dc
There was a problem hiding this comment.
Stale comment
Review verdict
REQUEST_CHANGES — consensus HIGH on stale journal finding
fnd-cites…plus CreateEffort/citescontract stickiness, and two+ independent HIGHs (opaque same-effort errors, missing writer/CLI foreign-blobnegatives, undocumentedCitation.blobsame-Effort rule).Adversarial DAG (4 perspectives + judge) on
91aec60...1559fd9. Models: HIGH=grok-4.5, MED/LOW=composer-2.5.Must-fix before merge
- Invalidate/supersede active finding
fnd-cites-and-citation-blob-skip-same-effort-checks(still teaches pre-fix behavior).- Make same-effort errors specific (
cites target ${id}…/Citation.blob ${id}…), not bare'Different effort'.- Keep
CreateEffort.strict()but makecitesrejection loud end-to-end (domain error + CLI pin); resolve Effort preset still advertisingcites.- Add writer + CLI negatives for foreign
Citation.blob; documentblobsame-Effort in skill reference.Coverage plan
- HIGH
writer.test.ts—WriteCitationrejects blob from another effort (negative)- HIGH
cli/effort.test.ts—CreateEffort+citesrejected athandleEffortWrite(negative)- HIGH
cli/effort.test.ts—WriteCitationforeignblobrejected (negative)- MED strengthen schemas Zod path assert; live GraphQL foreign reject;
relations(effortA, effortB)throw / effort-root no-throw- LOW planner mixed-list cites edge
Reviewer scoreboard
reviewer verdict signal correctness-and-contracts REQUEST_CHANGES strong test-coverage-robustness REQUEST_CHANGES strongest effort-graph-contracts REQUEST_CHANGES strong docs-and-positioning REQUEST_CHANGES good Sent by Cursor Automation: Flatbread PR Review
Apply a direct, newcomer-friendly writing style to every agent output, including responses, plans, PR descriptions, commits, documentation, and code comments. Require a brief explanation whenever a Flatbread-specific term is necessary. Co-authored-by: Cursor <cursoragent@cursor.com> Change-Id: Ia151e3589b3dcc602cf566f05940243ff116e4d8
Make cross-Effort Citation and Blob errors identify the failing field and id. Reject CreateEffort cites with a direct CLI error, remove the stale Effort cites reference, document the Blob ownership rule, and add writer and CLI regression coverage. Record that the earlier missing-validation Finding is now invalidated. Co-authored-by: Cursor <cursoragent@cursor.com> Change-Id: I9d9531e5e8ae75e6b48113fbfc6c5fc9b7180a51
There was a problem hiding this comment.
Stale comment
Review verdict
REQUEST_CHANGES — consensus HIGH that
CreateEffort+citesstill silently succeeds onplanMutation/writer.mutatewhile schema, CLI, and docs claim rejection.Same-Effort
cites/Citation.blobchecks, clearer errors, foreign-blob negatives, and skillreference.mdlook solid. The remaining contract hole is the library write path:createEffortGraphWriter().mutatetakesinput: anyand callsplanMutationwithoutEffortGraphMutationSchema, so programmatic/GraphQL callers can still passcitesand have them dropped.Chunk-bound feedback (priority)
- HIGH
planner.tsCreateEffort branch — reject'cites' in input(same message as CLI) or parse the mutation schema before planning.- MED
CreateEffortSchema.strict()/ CLI pre-parse guard — move enforcement to planner/writer so CLI is not the only fail-closed path.- MED Active Findings that still teach direct Blob cites or “13 mutations / 6 primitives” — invalidate or supersede so recall matches current skills.
Coverage plan
writer.test.ts— negative:writer.mutate({ type:'CreateEffort', cites:[...] })rejects; Effort file has nocitesplanner.test.ts— negative:planMutation(CreateEffort + cites)throws actionableCreateEffort does not accept citesliveServerEffortGraph.test.ts— negative: live-bridgeCreateEffort+citesfails closed (non-CLI path)schemas.test.ts— edge: failure text forCreateEffort+citesis actionable, not only opaqueunrecognized_keysReviewer scoreboard
- correctness-and-contracts: 5 findings, 3 coverage gaps, signal:HIGH
- test-coverage-robustness: 2 findings, 3 coverage gaps, signal:HIGH
- docs-and-positioning: 6 findings, 4 coverage gaps, signal:HIGH
- cli-and-runtime: 3 findings, 4 coverage gaps, signal:MED
- effort-graph-citation-invariants: 3 findings, 3 coverage gaps, signal:MED
Prior threads: same-Effort errors, foreign-blob tests, and skill reference updates look addressed; CreateEffort stickiness remains partially open on the writer path.
Sent by Cursor Automation: Flatbread PR Review
Fail closed for direct planner and writer callers instead of silently dropping an unsupported CreateEffort relation. Tests: pnpm exec ava packages/effort-graph/src/__tests__/planner.test.ts packages/effort-graph/src/__tests__/writer.test.ts Co-authored-by: Cursor <cursoragent@cursor.com> Change-Id: Ie64f029651430b3b638849edd5774f9904fcbfaa
Document same-Effort Citation and Blob links in the source and installed skills. Invalidate stale Findings and record that CreateEffort cites remains a programmatic-writer follow-up. Co-authored-by: Cursor <cursoragent@cursor.com> Change-Id: I695834471a811abca6b06595bfaf81d72332db5d
|
@Mergifyio queue |
Merge Queue Status
This pull request spent 3 minutes 18 seconds in the queue, including 2 minutes 57 seconds running CI. Required conditions to merge
|
There was a problem hiding this comment.
Review verdict
REQUEST_CHANGES — Consensus HIGH on a stale CreateEffort-enforcement Finding, plus independent HIGHs on Citation/Blob silent-drop (planner/schema) and missing live-bridge coverage of documented same-Effort / CreateEffort rejects.
Prior CreateEffort+cites silent-drop on planner/writer is closed at HEAD (planMutation throws). Remaining blockers are contract honesty + fail-closed Citation/Blob + GraphQL negative coverage.
Chunk-bound feedback (priority)
- HIGH Stale Finding still says planner/writer CreateEffort/
citesenforcement is a follow-up — rewrite or supersede. - HIGH
WriteCitation/WriteBlobstill Zod-strip unknown edge keys; plannerdeletes them — apply.strict()/ throw like CreateEffort. - HIGH Live GraphQL writer never asserts CreateEffort+
cites, foreign cite, or foreignCitation.blobrejects. - MED effort-modeling skill omits Issue from
citescreate list;relations()silently filters foreign cite targets.
Coverage plan
liveServerEffortGraph.test.ts— negative CreateEffort+cites, foreign blob, foreign cite; positive Finding→Citation resolveschemas.test.ts/planner.test.ts/writer.test.ts— WriteCitation/WriteBlob +citesrejecteffort.test.ts— WriteCitation+citesCLI error; relations not-in-effort / foreign-cite edge
Reviewer scoreboard
- correctness-and-contracts: 3 findings, signal:HIGH
- test-coverage-robustness: 5 findings, signal:HIGH
- docs-and-positioning: 2 findings, signal:HIGH
- effort-graph-citation-blob: 4 findings, signal:MED
- cli-and-runtime: 1 finding, signal:MED
Models: Grok 4.5 High (HIGH), Composer 2.5 (MED/LOW).
Sent by Cursor Automation: Flatbread PR Review
| - fnd-skill-and-hard-constraint-still-teach-13-mutatio--gvg2btns0q7rp0eq | ||
| --- | ||
|
|
||
| The skills and related Decision, Issue, and Constraint now describe eight record types, fifteen mutations, and the Citation-to-optional-Blob path. Direct Blob citations are not supported. Cross-Effort `cites` and `Citation.blob` links are rejected. `CreateEffort` rejects `cites` when callers use the CLI or mutation schema; planner and writer enforcement remains a follow-up so programmatic callers cannot silently drop the field. This Finding replaces the prior audits and retrospective where their earlier claims no longer match the current contract. |
There was a problem hiding this comment.
HIGH (consensus) — This active Finding says CreateEffort rejects cites only via CLI/mutation schema and that "planner and writer enforcement remains a follow-up." At HEAD, planMutation already throws CreateEffort does not accept cites… and writer tests cover it. Agent-facing memory that invents remaining work will send follow-up PRs at a closed gap.
Minimal fix: Rewrite the body to state CLI, schema, planner, and writer all reject CreateEffort/cites (or supersede with a Finding that does).
| if (EPISTEMIC_CREATE.has(kind)) assertCites(get, raw.effort, raw.cites); | ||
| const fm: Record<string, unknown> = { | ||
| ...raw, | ||
| id, |
There was a problem hiding this comment.
HIGH — For Citation/Blob creates, mistaken cites / edge keys are never validated (EPISTEMIC_CREATE skips these kinds), then silently deleted before write. Callers get a successful mutate with the mistake dropped — the same silent-drop class just closed for CreateEffort.
Minimal fix: Throw EffortGraphValidationError when those keys are present on Citation/Blob creates (CreateEffort posture).
| ...common, | ||
| slug: z.string().optional(), | ||
| }) | ||
| .strict(); |
There was a problem hiding this comment.
HIGH — Only CreateEffortSchema is .strict(). WriteCitationSchema / WriteBlobSchema still Zod-strip unrecognized keys such as cites / derives_from, so CLI parse can hide the mistake before the planner ever sees it.
Minimal fix: Apply .strict() (or an explicit reject) on WriteCitation/WriteBlob, and add schema/CLI negative tests.
| t.deepEqual(response.data?.allBlobs, [{ id: blobId, title: 'Payload' }]); | ||
| } | ||
| ); | ||
|
|
There was a problem hiding this comment.
HIGH — New live-bridge coverage is Citation→Blob happy-path shape only. Documented rejects (CreateEffort+cites, foreign cite, foreign Citation.blob) and Finding→Citation resolve after commit are unexercised on server.effortGraph.writer / GraphQL, so bridge/ref regressions would not fail CI.
Minimal fix: Add parallel negative mutates plus a positive allFindings { cites { id } } resolve.
| if ('cites' in input) | ||
| throw new EffortGraphValidationError( | ||
| 'CreateEffort does not accept cites; create the Effort before its Citations.' | ||
| ); |
There was a problem hiding this comment.
HIGH (coverage) — CreateEffort/cites reject is closed in planner/writer/CLI unit tests, but the no-Zod live-server writer path still lacks an assertion for this contract.
Minimal fix: In liveServerEffortGraph.test.ts, assert server.effortGraph.writer.mutate({ type: 'CreateEffort', …, cites: [...] }) throws the planner message.
| `role` when needed. | ||
| 3. **Create the record** — pass `cites: ["<cit-id>"]` when creating the | ||
| Finding, Decision, Constraint, or Risk. You cannot add a citation later, | ||
| so create the Citation first. |
There was a problem hiding this comment.
MED — External-evidence step 3 lists Finding/Decision/Constraint/Risk but omits Issue, which schemas/cites[] allow. Agents following this skill will under-use Issue citations.
Minimal fix: Add Issue to the create list; sync via pnpm skills:sync into packages/effort-graph/skills/….
| source?.kind === 'effort' && fromId === effortId | ||
| ? true | ||
| : source?.frontmatter.effort === effortId; | ||
| if (!source || !sourceInEffort) |
There was a problem hiding this comment.
MED — Happy path covers Effort-as-relations-root empty cites; foreign cite targets are filtered with no anomaly when target.frontmatter.effort !== effortId. Corrupt/pre-fix data under-counts silently.
Minimal fix: Assert not-in-effort when fromId is another Effort; add a seeded foreign-cite case and either surface an anomaly/filtered-count hint or document intentional defensive filtering.
| throw new EffortGraphValidationError( | ||
| 'CreateEffort does not accept cites; create the Effort before its Citations.' | ||
| ); | ||
| const input = EffortGraphMutationSchema.parse(raw); |
There was a problem hiding this comment.
MED — CLI fail-closes CreateEffort+cites with a clear error, but WriteCitation+cites still succeeds after Zod strip until schemas/planner reject. Once Write* schemas are strict (or CLI mirrors this guard), assert a discoverable validation error at the write handler.


Follow-up to the Citation and Blob work in #224 and #225.
What this PR changes
How to attach a source
WriteBlobwhen needed.WriteCitationfor the URL or source.cites: ["<citation-id>"].Create the Citation before the record that uses it; citations cannot be added later.
Validation
pnpm -F @flatbread/effort-graph test(104 passed)pnpm exec ava packages/flatbread/src/cli/effort.test.ts packages/flatbread/src/graphql/liveServerEffortGraph.test.ts(15 passed)pnpm lintpnpm skills:check