sha256 98f8d72a3133…
Shared documents + deterministic tools
Plain Markdown.
Precise tooling.
Shared Markdown plans with acceptance criteria, read and edited by a TypeScript library and CLI.
Markdown protocol · TypeScript library · CLI · MIT licensed
Create
Start with one readable file.
Numbered acceptance criteria give every requirement a stable ID from the first draft.
01 Markdown source
---
id: 1792612920_search-feedback-criteria
created_at: 2026-10-21T20:02:00Z
updated_at: 2026-10-21T20:02:00Z
revision: 1
status: pending
protocol: "0.2"
priority: medium
repositories:
- example/search
error_codes:
- repository: example/search
registry: docs/errors.md
codes:
- 4100
- SEARCH_FILTER_INVALID
---
# Search feedback
Explain empty search results and give people a useful next action.
## Acceptance criteria
- AC-01: Empty search results explain why nothing matched.
- AC-02: The empty state offers one next action, such as clearing the active filters.
- AC-03: The explanation and the action work with a keyboard and a screen reader.02 Complete API result
createPlan(options) {
"directory": "jg-plans/search-feedback",
"plan": {
"name": "search-feedback-criteria",
"source": "---\nid: 1792612920_search-feedback-criteria\ncreated_at: 2026-10-21T20:02:00Z\nupdated_at: 2026-10-21T20:02:00Z\nrevision: 1\nstatus: pending\nprotocol: \"0.2\"\npriority: medium\nrepositories:\n - example/search\nerror_codes:\n - repository: example/search\n registry: docs/errors.md\n codes:\n - 4100\n - SEARCH_FILTER_INVALID\n---\n# Search feedback\n\nExplain empty search results and give people a useful next action.\n\n## Acceptance criteria\n\n- AC-01: Empty search results explain why nothing matched.\n- AC-02: The empty state offers one next action, such as clearing the active filters.\n- AC-03: The explanation and the action work with a keyboard and a screen reader.\n",
"body": "# Search feedback\n\nExplain empty search results and give people a useful next action.\n\n## Acceptance criteria\n\n- AC-01: Empty search results explain why nothing matched.\n- AC-02: The empty state offers one next action, such as clearing the active filters.\n- AC-03: The explanation and the action work with a keyboard and a screen reader.\n",
"frontmatter": {
"start": 0,
"contentStart": 4,
"contentEnd": 331,
"end": 335,
"closed": true
},
"rawMetadata": {
"id": "1792612920_search-feedback-criteria",
"created_at": "2026-10-21T20:02:00Z",
"updated_at": "2026-10-21T20:02:00Z",
"revision": 1,
"status": "pending",
"protocol": "0.2",
"priority": "medium",
"repositories": [
"example/search"
],
"error_codes": [
{
"repository": "example/search",
"registry": "docs/errors.md",
"codes": [
4100,
"SEARCH_FILTER_INVALID"
]
}
]
},
"metadata": {
"id": "1792612920_search-feedback-criteria",
"created_at": "2026-10-21T20:02:00Z",
"updated_at": "2026-10-21T20:02:00Z",
"revision": 1,
"status": "pending",
"protocol": "0.2",
"priority": "medium",
"repositories": [
"example/search"
],
"error_codes": [
{
"repository": "example/search",
"registry": "docs/errors.md",
"codes": [
4100,
"SEARCH_FILTER_INVALID"
]
}
]
},
"preamble": {
"start": 0,
"end": 422
},
"sections": [
{
"title": "Acceptance criteria",
"heading": {
"start": 422,
"end": 445
},
"body": {
"start": 445,
"end": 673
}
}
],
"protocol": "0.2",
"criteria": [
{
"id": "AC-01",
"idText": "AC-01",
"text": "Empty search results explain why nothing matched.",
"location": {
"offset": 448,
"line": 24,
"column": 3
},
"range": {
"start": 446,
"end": 504
}
},
{
"id": "AC-02",
"idText": "AC-02",
"text": "The empty state offers one next action, such as clearing the active filters.",
"location": {
"offset": 507,
"line": 25,
"column": 3
},
"range": {
"start": 505,
"end": 590
}
},
{
"id": "AC-03",
"idText": "AC-03",
"text": "The explanation and the action work with a keyboard and a screen reader.",
"location": {
"offset": 593,
"line": 26,
"column": 3
},
"range": {
"start": 591,
"end": 672
}
}
],
"diagnostics": [],
"sha256": "946816c6f612bb4f868842168bcfd802637e84a40d77c238904afb0f9ba3cbbf"
},
"milestones": null,
"diagnostics": []
}Consumer-derived summary
{
"plan": "readable",
"milestones": "missing",
"revision": 1,
"status": "pending",
"criteria": [
"AC-01",
"AC-02",
"AC-03"
]
}Public API call · excerpt
const created = await createPlan({
root: './jg-plans',
slug: 'search-feedback',
body, // the Markdown body shown below
metadata, // the displayed example metadata, including protocol: '0.2'
});Inspect
Read what is actually there.
One selected directory returns metadata, criteria and diagnostics without recreating anything.
01 Markdown source
---
id: 1792612920_search-feedback-criteria
created_at: 2026-10-21T20:02:00Z
updated_at: 2026-10-21T20:02:00Z
revision: 1
status: pending
protocol: "0.2"
priority: medium
repositories:
- example/search
error_codes:
- repository: example/search
registry: docs/errors.md
codes:
- 4100
- SEARCH_FILTER_INVALID
---
# Search feedback
Explain empty search results and give people a useful next action.
## Acceptance criteria
- AC-01: Empty search results explain why nothing matched.
- AC-02: The empty state offers one next action, such as clearing the active filters.
- AC-03: The explanation and the action work with a keyboard and a screen reader.02 Complete API result
inspectPlan(directory) {
"directory": "jg-plans/search-feedback",
"plan": {
"state": "readable",
"path": "jg-plans/search-feedback/plan.md",
"sha256": "946816c6f612bb4f868842168bcfd802637e84a40d77c238904afb0f9ba3cbbf",
"value": {
"name": "search-feedback-criteria",
"source": "---\nid: 1792612920_search-feedback-criteria\ncreated_at: 2026-10-21T20:02:00Z\nupdated_at: 2026-10-21T20:02:00Z\nrevision: 1\nstatus: pending\nprotocol: \"0.2\"\npriority: medium\nrepositories:\n - example/search\nerror_codes:\n - repository: example/search\n registry: docs/errors.md\n codes:\n - 4100\n - SEARCH_FILTER_INVALID\n---\n# Search feedback\n\nExplain empty search results and give people a useful next action.\n\n## Acceptance criteria\n\n- AC-01: Empty search results explain why nothing matched.\n- AC-02: The empty state offers one next action, such as clearing the active filters.\n- AC-03: The explanation and the action work with a keyboard and a screen reader.\n",
"body": "# Search feedback\n\nExplain empty search results and give people a useful next action.\n\n## Acceptance criteria\n\n- AC-01: Empty search results explain why nothing matched.\n- AC-02: The empty state offers one next action, such as clearing the active filters.\n- AC-03: The explanation and the action work with a keyboard and a screen reader.\n",
"frontmatter": {
"start": 0,
"contentStart": 4,
"contentEnd": 331,
"end": 335,
"closed": true
},
"rawMetadata": {
"id": "1792612920_search-feedback-criteria",
"created_at": "2026-10-21T20:02:00Z",
"updated_at": "2026-10-21T20:02:00Z",
"revision": 1,
"status": "pending",
"protocol": "0.2",
"priority": "medium",
"repositories": [
"example/search"
],
"error_codes": [
{
"repository": "example/search",
"registry": "docs/errors.md",
"codes": [
4100,
"SEARCH_FILTER_INVALID"
]
}
]
},
"metadata": {
"id": "1792612920_search-feedback-criteria",
"created_at": "2026-10-21T20:02:00Z",
"updated_at": "2026-10-21T20:02:00Z",
"revision": 1,
"status": "pending",
"protocol": "0.2",
"priority": "medium",
"repositories": [
"example/search"
],
"error_codes": [
{
"repository": "example/search",
"registry": "docs/errors.md",
"codes": [
4100,
"SEARCH_FILTER_INVALID"
]
}
]
},
"preamble": {
"start": 0,
"end": 422
},
"sections": [
{
"title": "Acceptance criteria",
"heading": {
"start": 422,
"end": 445
},
"body": {
"start": 445,
"end": 673
}
}
],
"protocol": "0.2",
"criteria": [
{
"id": "AC-01",
"idText": "AC-01",
"text": "Empty search results explain why nothing matched.",
"location": {
"offset": 448,
"line": 24,
"column": 3
},
"range": {
"start": 446,
"end": 504
}
},
{
"id": "AC-02",
"idText": "AC-02",
"text": "The empty state offers one next action, such as clearing the active filters.",
"location": {
"offset": 507,
"line": 25,
"column": 3
},
"range": {
"start": 505,
"end": 590
}
},
{
"id": "AC-03",
"idText": "AC-03",
"text": "The explanation and the action work with a keyboard and a screen reader.",
"location": {
"offset": 593,
"line": 26,
"column": 3
},
"range": {
"start": 591,
"end": 672
}
}
],
"diagnostics": []
}
},
"milestones": {
"state": "missing",
"path": "jg-plans/search-feedback/milestones.md"
},
"criteria": {
"protocol": "0.2",
"criteria": [
{
"id": "AC-01",
"text": "Empty search results explain why nothing matched.",
"location": {
"offset": 448,
"line": 24,
"column": 3
},
"milestones": [],
"progress": "pending"
},
{
"id": "AC-02",
"text": "The empty state offers one next action, such as clearing the active filters.",
"location": {
"offset": 507,
"line": 25,
"column": 3
},
"milestones": [],
"progress": "pending"
},
{
"id": "AC-03",
"text": "The explanation and the action work with a keyboard and a screen reader.",
"location": {
"offset": 593,
"line": 26,
"column": 3
},
"milestones": [],
"progress": "pending"
}
],
"diagnostics": []
},
"diagnostics": [
{
"document": "milestones",
"code": "MISSING_MILESTONES",
"severity": "info",
"message": "The optional milestone file is absent."
}
]
}Consumer-derived summary
{
"plan": "readable",
"milestones": "missing",
"metadata": {
"id": "1792612920_search-feedback-criteria",
"created_at": "2026-10-21T20:02:00Z",
"updated_at": "2026-10-21T20:02:00Z",
"revision": 1,
"status": "pending",
"protocol": "0.2",
"priority": "medium",
"repositories": [
"example/search"
],
"error_codes": [
{
"repository": "example/search",
"registry": "docs/errors.md",
"codes": [
4100,
"SEARCH_FILTER_INVALID"
]
}
]
},
"criteria": [
{
"id": "AC-01",
"progress": "pending"
},
{
"id": "AC-02",
"progress": "pending"
},
{
"id": "AC-03",
"progress": "pending"
}
]
}Public API call · excerpt
const observed = await inspectPlan(created.directory);Update
Make an explicit, guarded edit.
Milestones name the criteria they deliver, and each criterion's progress follows their status.
01 Markdown source
---
id: 1792612920_search-feedback-criteria
created_at: 2026-10-21T20:02:00Z
updated_at: "2026-10-21T20:04:00Z"
revision: 1
status: "in progress"
protocol: "0.2"
priority: medium
repositories:
- example/search
error_codes:
- repository: example/search
registry: docs/errors.md
codes:
- 4100
- SEARCH_FILTER_INVALID
---
# Search feedback
Explain empty search results and give people a useful next action.
## Acceptance criteria
- AC-01: Empty search results explain why nothing matched.
- AC-02: The empty state offers one next action, such as clearing the active filters.
- AC-03: The explanation and the action work with a keyboard and a screen reader.02 Complete API result
inspectPlan(directory) {
"directory": "jg-plans/search-feedback",
"plan": {
"state": "readable",
"path": "jg-plans/search-feedback/plan.md",
"sha256": "98f8d72a31336f3c7f0f98b709f8789d300f9047eb10195beabcbb52d57af116",
"value": {
"name": "search-feedback-criteria",
"source": "---\nid: 1792612920_search-feedback-criteria\ncreated_at: 2026-10-21T20:02:00Z\nupdated_at: \"2026-10-21T20:04:00Z\"\nrevision: 1\nstatus: \"in progress\"\nprotocol: \"0.2\"\npriority: medium\nrepositories:\n - example/search\nerror_codes:\n - repository: example/search\n registry: docs/errors.md\n codes:\n - 4100\n - SEARCH_FILTER_INVALID\n---\n# Search feedback\n\nExplain empty search results and give people a useful next action.\n\n## Acceptance criteria\n\n- AC-01: Empty search results explain why nothing matched.\n- AC-02: The empty state offers one next action, such as clearing the active filters.\n- AC-03: The explanation and the action work with a keyboard and a screen reader.\n",
"body": "# Search feedback\n\nExplain empty search results and give people a useful next action.\n\n## Acceptance criteria\n\n- AC-01: Empty search results explain why nothing matched.\n- AC-02: The empty state offers one next action, such as clearing the active filters.\n- AC-03: The explanation and the action work with a keyboard and a screen reader.\n",
"frontmatter": {
"start": 0,
"contentStart": 4,
"contentEnd": 339,
"end": 343,
"closed": true
},
"rawMetadata": {
"id": "1792612920_search-feedback-criteria",
"created_at": "2026-10-21T20:02:00Z",
"updated_at": "2026-10-21T20:04:00Z",
"revision": 1,
"status": "in progress",
"protocol": "0.2",
"priority": "medium",
"repositories": [
"example/search"
],
"error_codes": [
{
"repository": "example/search",
"registry": "docs/errors.md",
"codes": [
4100,
"SEARCH_FILTER_INVALID"
]
}
]
},
"metadata": {
"id": "1792612920_search-feedback-criteria",
"created_at": "2026-10-21T20:02:00Z",
"updated_at": "2026-10-21T20:04:00Z",
"revision": 1,
"status": "in progress",
"protocol": "0.2",
"priority": "medium",
"repositories": [
"example/search"
],
"error_codes": [
{
"repository": "example/search",
"registry": "docs/errors.md",
"codes": [
4100,
"SEARCH_FILTER_INVALID"
]
}
]
},
"preamble": {
"start": 0,
"end": 430
},
"sections": [
{
"title": "Acceptance criteria",
"heading": {
"start": 430,
"end": 453
},
"body": {
"start": 453,
"end": 681
}
}
],
"protocol": "0.2",
"criteria": [
{
"id": "AC-01",
"idText": "AC-01",
"text": "Empty search results explain why nothing matched.",
"location": {
"offset": 456,
"line": 24,
"column": 3
},
"range": {
"start": 454,
"end": 512
}
},
{
"id": "AC-02",
"idText": "AC-02",
"text": "The empty state offers one next action, such as clearing the active filters.",
"location": {
"offset": 515,
"line": 25,
"column": 3
},
"range": {
"start": 513,
"end": 598
}
},
{
"id": "AC-03",
"idText": "AC-03",
"text": "The explanation and the action work with a keyboard and a screen reader.",
"location": {
"offset": 601,
"line": 26,
"column": 3
},
"range": {
"start": 599,
"end": 680
}
}
],
"diagnostics": []
}
},
"milestones": {
"state": "readable",
"path": "jg-plans/search-feedback/milestones.md",
"sha256": "46d4f0bd33d023830f4b9fc772cdd817b46d239c23166c3016fd0d08294f307a",
"value": {
"source": "---\njg_skills:\n plan_id: \"1792612920_search-feedback-criteria\"\n plan_revision: 1\n plan_content_sha256: 21da41286dc3d021cdc6adddef1283d7affe70b92832824bd00244ff1b9058d8\n---\n\n## Milestone 1: Explain empty results\n**Status:** done\n**Milestone ID:** 3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34\n**Acceptance criteria:** AC-01\n\nAdd the explanation to the empty results view. Review it in both themes.\n\n## Milestone 2: Offer a next action\n**Status:** in progress\n**Milestone ID:** 8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15\n**Acceptance criteria:** AC-02, AC-03\n\nAdd the clear-filters action, then check it with a keyboard and a screen reader.\n",
"body": "\n## Milestone 1: Explain empty results\n**Status:** done\n**Milestone ID:** 3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34\n**Acceptance criteria:** AC-01\n\nAdd the explanation to the empty results view. Review it in both themes.\n\n## Milestone 2: Offer a next action\n**Status:** in progress\n**Milestone ID:** 8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15\n**Acceptance criteria:** AC-02, AC-03\n\nAdd the clear-filters action, then check it with a keyboard and a screen reader.\n",
"frontmatter": {
"start": 0,
"contentStart": 4,
"contentEnd": 171,
"end": 175,
"closed": true
},
"rawMetadata": {
"jg_skills": {
"plan_id": "1792612920_search-feedback-criteria",
"plan_revision": 1,
"plan_content_sha256": "21da41286dc3d021cdc6adddef1283d7affe70b92832824bd00244ff1b9058d8"
}
},
"preamble": {
"start": 0,
"end": 176
},
"sections": [
{
"title": "Milestone 1: Explain empty results",
"heading": {
"start": 176,
"end": 214
},
"body": {
"start": 214,
"end": 392
}
},
{
"title": "Milestone 2: Offer a next action",
"heading": {
"start": 392,
"end": 428
},
"body": {
"start": 428,
"end": 627
}
}
],
"overviewTables": [],
"milestones": [
{
"ordinal": 1,
"ordinalText": "1",
"title": "Explain empty results",
"status": "done",
"id": "3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34",
"location": {
"offset": 176,
"line": 8,
"column": 1
},
"headingEnd": 214,
"end": 392,
"body": "**Status:** done\n**Milestone ID:** 3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34\n**Acceptance criteria:** AC-01\n\nAdd the explanation to the empty results view. Review it in both themes.\n\n",
"statusMarkers": [
{
"value": "done",
"location": {
"offset": 214,
"line": 9,
"column": 1
},
"valueStart": 226,
"valueEnd": 230,
"lineStart": 214,
"lineEnd": 231
}
],
"idMarkers": [
{
"value": "3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34",
"location": {
"offset": 231,
"line": 10,
"column": 1
},
"valueStart": 249,
"valueEnd": 285,
"lineStart": 231,
"lineEnd": 286
}
],
"criteria": [
"AC-01"
],
"criteriaMarkers": [
{
"value": "AC-01",
"location": {
"offset": 286,
"line": 11,
"column": 1
},
"valueStart": 311,
"valueEnd": 316,
"lineStart": 286,
"lineEnd": 317
}
]
},
{
"ordinal": 2,
"ordinalText": "2",
"title": "Offer a next action",
"status": "in progress",
"id": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15",
"location": {
"offset": 392,
"line": 15,
"column": 1
},
"headingEnd": 428,
"end": 627,
"body": "**Status:** in progress\n**Milestone ID:** 8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15\n**Acceptance criteria:** AC-02, AC-03\n\nAdd the clear-filters action, then check it with a keyboard and a screen reader.\n",
"statusMarkers": [
{
"value": "in progress",
"location": {
"offset": 428,
"line": 16,
"column": 1
},
"valueStart": 440,
"valueEnd": 451,
"lineStart": 428,
"lineEnd": 452
}
],
"idMarkers": [
{
"value": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15",
"location": {
"offset": 452,
"line": 17,
"column": 1
},
"valueStart": 470,
"valueEnd": 506,
"lineStart": 452,
"lineEnd": 507
}
],
"criteria": [
"AC-02",
"AC-03"
],
"criteriaMarkers": [
{
"value": "AC-02, AC-03",
"location": {
"offset": 507,
"line": 18,
"column": 1
},
"valueStart": 532,
"valueEnd": 544,
"lineStart": 507,
"lineEnd": 545
}
]
}
],
"overview": [],
"overviewPresent": false,
"diagnostics": []
}
},
"criteria": {
"protocol": "0.2",
"criteria": [
{
"id": "AC-01",
"text": "Empty search results explain why nothing matched.",
"location": {
"offset": 456,
"line": 24,
"column": 3
},
"milestones": [
{
"ordinal": 1,
"id": "3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34"
}
],
"progress": "done"
},
{
"id": "AC-02",
"text": "The empty state offers one next action, such as clearing the active filters.",
"location": {
"offset": 515,
"line": 25,
"column": 3
},
"milestones": [
{
"ordinal": 2,
"id": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15"
}
],
"progress": "in progress"
},
{
"id": "AC-03",
"text": "The explanation and the action work with a keyboard and a screen reader.",
"location": {
"offset": 601,
"line": 26,
"column": 3
},
"milestones": [
{
"ordinal": 2,
"id": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15"
}
],
"progress": "in progress"
}
],
"diagnostics": []
},
"diagnostics": []
}- AC-01done
- AC-02in progress
- AC-03in progress
Mutation and check results
{
"directory": "jg-plans/search-feedback",
"document": "plan",
"changed": true,
"sha256": "98f8d72a31336f3c7f0f98b709f8789d300f9047eb10195beabcbb52d57af116",
"diagnostics": [],
"assignedMilestoneIds": []
}{
"directory": "jg-plans/search-feedback",
"document": "milestones",
"changed": true,
"sha256": "46d4f0bd33d023830f4b9fc772cdd817b46d239c23166c3016fd0d08294f307a",
"diagnostics": [],
"assignedMilestoneIds": []
}Consumer-derived summary
{
"revision": 1,
"status": "in progress",
"milestones": [
{
"id": "3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34",
"title": "Explain empty results",
"status": "done"
},
{
"id": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15",
"title": "Offer a next action",
"status": "in progress"
}
],
"criteria": [
{
"id": "AC-01",
"milestones": [
1
],
"progress": "done"
},
{
"id": "AC-02",
"milestones": [
2
],
"progress": "in progress"
},
{
"id": "AC-03",
"milestones": [
2
],
"progress": "in progress"
}
],
"binding": "current"
}Public API call · excerpt
if (observed.plan.state === 'readable') {
await updatePlan(directory, {
document: 'plan',
expectedSha256: observed.plan.sha256,
set: { status: 'in progress', updated_at },
});
}
// Add the explicitly prepared milestone source as a separate guarded write.
await writePlanDocument(directory, {
document: 'milestones',
expectedSha256: null,
source: milestoneSource,
});
const current = await inspectPlan(directory);Revise
Change the intent. Keep the context.
A new criterion stays flagged by check until a milestone delivers it; existing IDs stay byte-for-byte.
---id: 1792612920_search-feedback-criteriacreated_at: 2026-10-21T20:02:00Zupdated_at: "2026-10-21T20:04:00Z"Removed line: revision: 1Added line: revision: 2status: "in progress"protocol: "0.2"priority: mediumrepositories: - example/searcherror_codes: - repository: example/search registry: docs/errors.md codes: - 4100 - SEARCH_FILTER_INVALID---# Search feedback Explain empty search results and give people a useful next action. ## Acceptance criteria - AC-01: Empty search results explain why nothing matched.- AC-02: The empty state offers one next action, such as clearing the active filters.- AC-03: The explanation and the action work with a keyboard and a screen reader.Added line: - AC-04: Search suggests spelling corrections.---id: 1792612920_search-feedback-criteriacreated_at: 2026-10-21T20:02:00Zupdated_at: "2026-10-21T20:04:00Z"Removed line: revision: 1status: "in progress"protocol: "0.2"priority: mediumrepositories: - example/searcherror_codes: - repository: example/search registry: docs/errors.md codes: - 4100 - SEARCH_FILTER_INVALID---# Search feedback Explain empty search results and give people a useful next action. ## Acceptance criteria - AC-01: Empty search results explain why nothing matched.- AC-02: The empty state offers one next action, such as clearing the active filters.- AC-03: The explanation and the action work with a keyboard and a screen reader.---id: 1792612920_search-feedback-criteriacreated_at: 2026-10-21T20:02:00Zupdated_at: "2026-10-21T20:04:00Z"Added line: revision: 2status: "in progress"protocol: "0.2"priority: mediumrepositories: - example/searcherror_codes: - repository: example/search registry: docs/errors.md codes: - 4100 - SEARCH_FILTER_INVALID---# Search feedback Explain empty search results and give people a useful next action. ## Acceptance criteria - AC-01: Empty search results explain why nothing matched.- AC-02: The empty state offers one next action, such as clearing the active filters.- AC-03: The explanation and the action work with a keyboard and a screen reader.Added line: - AC-04: Search suggests spelling corrections.check · plan.md line 27
UNASSIGNED_CRITERIONAC-04
No milestone delivers AC-04 until the next breakdown.
Milestones · exact original bytes
Explain empty resultsdone
3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34
Offer a next actionin progress
8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15
Skills binding · stale after revision
Done markers stay; skills wait for a fresh breakdown before executing.
Actual revision observation
---
id: 1792612920_search-feedback-criteria
created_at: 2026-10-21T20:02:00Z
updated_at: "2026-10-21T20:04:00Z"
revision: 2
status: "in progress"
protocol: "0.2"
priority: medium
repositories:
- example/search
error_codes:
- repository: example/search
registry: docs/errors.md
codes:
- 4100
- SEARCH_FILTER_INVALID
---
# Search feedback
Explain empty search results and give people a useful next action.
## Acceptance criteria
- AC-01: Empty search results explain why nothing matched.
- AC-02: The empty state offers one next action, such as clearing the active filters.
- AC-03: The explanation and the action work with a keyboard and a screen reader.
- AC-04: Search suggests spelling corrections.inspectPlan(directory) · complete result
{
"directory": "jg-plans/search-feedback",
"plan": {
"state": "readable",
"path": "jg-plans/search-feedback/plan.md",
"sha256": "99cbfac6e98dcfe04052fef6a5031e586464f8be578d0a491691158b8c5beb4a",
"value": {
"name": "search-feedback-criteria",
"source": "---\nid: 1792612920_search-feedback-criteria\ncreated_at: 2026-10-21T20:02:00Z\nupdated_at: \"2026-10-21T20:04:00Z\"\nrevision: 2\nstatus: \"in progress\"\nprotocol: \"0.2\"\npriority: medium\nrepositories:\n - example/search\nerror_codes:\n - repository: example/search\n registry: docs/errors.md\n codes:\n - 4100\n - SEARCH_FILTER_INVALID\n---\n# Search feedback\n\nExplain empty search results and give people a useful next action.\n\n## Acceptance criteria\n\n- AC-01: Empty search results explain why nothing matched.\n- AC-02: The empty state offers one next action, such as clearing the active filters.\n- AC-03: The explanation and the action work with a keyboard and a screen reader.\n- AC-04: Search suggests spelling corrections.\n",
"body": "# Search feedback\n\nExplain empty search results and give people a useful next action.\n\n## Acceptance criteria\n\n- AC-01: Empty search results explain why nothing matched.\n- AC-02: The empty state offers one next action, such as clearing the active filters.\n- AC-03: The explanation and the action work with a keyboard and a screen reader.\n- AC-04: Search suggests spelling corrections.\n",
"frontmatter": {
"start": 0,
"contentStart": 4,
"contentEnd": 339,
"end": 343,
"closed": true
},
"rawMetadata": {
"id": "1792612920_search-feedback-criteria",
"created_at": "2026-10-21T20:02:00Z",
"updated_at": "2026-10-21T20:04:00Z",
"revision": 2,
"status": "in progress",
"protocol": "0.2",
"priority": "medium",
"repositories": [
"example/search"
],
"error_codes": [
{
"repository": "example/search",
"registry": "docs/errors.md",
"codes": [
4100,
"SEARCH_FILTER_INVALID"
]
}
]
},
"metadata": {
"id": "1792612920_search-feedback-criteria",
"created_at": "2026-10-21T20:02:00Z",
"updated_at": "2026-10-21T20:04:00Z",
"revision": 2,
"status": "in progress",
"protocol": "0.2",
"priority": "medium",
"repositories": [
"example/search"
],
"error_codes": [
{
"repository": "example/search",
"registry": "docs/errors.md",
"codes": [
4100,
"SEARCH_FILTER_INVALID"
]
}
]
},
"preamble": {
"start": 0,
"end": 430
},
"sections": [
{
"title": "Acceptance criteria",
"heading": {
"start": 430,
"end": 453
},
"body": {
"start": 453,
"end": 728
}
}
],
"protocol": "0.2",
"criteria": [
{
"id": "AC-01",
"idText": "AC-01",
"text": "Empty search results explain why nothing matched.",
"location": {
"offset": 456,
"line": 24,
"column": 3
},
"range": {
"start": 454,
"end": 512
}
},
{
"id": "AC-02",
"idText": "AC-02",
"text": "The empty state offers one next action, such as clearing the active filters.",
"location": {
"offset": 515,
"line": 25,
"column": 3
},
"range": {
"start": 513,
"end": 598
}
},
{
"id": "AC-03",
"idText": "AC-03",
"text": "The explanation and the action work with a keyboard and a screen reader.",
"location": {
"offset": 601,
"line": 26,
"column": 3
},
"range": {
"start": 599,
"end": 680
}
},
{
"id": "AC-04",
"idText": "AC-04",
"text": "Search suggests spelling corrections.",
"location": {
"offset": 683,
"line": 27,
"column": 3
},
"range": {
"start": 681,
"end": 727
}
}
],
"diagnostics": []
}
},
"milestones": {
"state": "readable",
"path": "jg-plans/search-feedback/milestones.md",
"sha256": "46d4f0bd33d023830f4b9fc772cdd817b46d239c23166c3016fd0d08294f307a",
"value": {
"source": "---\njg_skills:\n plan_id: \"1792612920_search-feedback-criteria\"\n plan_revision: 1\n plan_content_sha256: 21da41286dc3d021cdc6adddef1283d7affe70b92832824bd00244ff1b9058d8\n---\n\n## Milestone 1: Explain empty results\n**Status:** done\n**Milestone ID:** 3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34\n**Acceptance criteria:** AC-01\n\nAdd the explanation to the empty results view. Review it in both themes.\n\n## Milestone 2: Offer a next action\n**Status:** in progress\n**Milestone ID:** 8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15\n**Acceptance criteria:** AC-02, AC-03\n\nAdd the clear-filters action, then check it with a keyboard and a screen reader.\n",
"body": "\n## Milestone 1: Explain empty results\n**Status:** done\n**Milestone ID:** 3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34\n**Acceptance criteria:** AC-01\n\nAdd the explanation to the empty results view. Review it in both themes.\n\n## Milestone 2: Offer a next action\n**Status:** in progress\n**Milestone ID:** 8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15\n**Acceptance criteria:** AC-02, AC-03\n\nAdd the clear-filters action, then check it with a keyboard and a screen reader.\n",
"frontmatter": {
"start": 0,
"contentStart": 4,
"contentEnd": 171,
"end": 175,
"closed": true
},
"rawMetadata": {
"jg_skills": {
"plan_id": "1792612920_search-feedback-criteria",
"plan_revision": 1,
"plan_content_sha256": "21da41286dc3d021cdc6adddef1283d7affe70b92832824bd00244ff1b9058d8"
}
},
"preamble": {
"start": 0,
"end": 176
},
"sections": [
{
"title": "Milestone 1: Explain empty results",
"heading": {
"start": 176,
"end": 214
},
"body": {
"start": 214,
"end": 392
}
},
{
"title": "Milestone 2: Offer a next action",
"heading": {
"start": 392,
"end": 428
},
"body": {
"start": 428,
"end": 627
}
}
],
"overviewTables": [],
"milestones": [
{
"ordinal": 1,
"ordinalText": "1",
"title": "Explain empty results",
"status": "done",
"id": "3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34",
"location": {
"offset": 176,
"line": 8,
"column": 1
},
"headingEnd": 214,
"end": 392,
"body": "**Status:** done\n**Milestone ID:** 3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34\n**Acceptance criteria:** AC-01\n\nAdd the explanation to the empty results view. Review it in both themes.\n\n",
"statusMarkers": [
{
"value": "done",
"location": {
"offset": 214,
"line": 9,
"column": 1
},
"valueStart": 226,
"valueEnd": 230,
"lineStart": 214,
"lineEnd": 231
}
],
"idMarkers": [
{
"value": "3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34",
"location": {
"offset": 231,
"line": 10,
"column": 1
},
"valueStart": 249,
"valueEnd": 285,
"lineStart": 231,
"lineEnd": 286
}
],
"criteria": [
"AC-01"
],
"criteriaMarkers": [
{
"value": "AC-01",
"location": {
"offset": 286,
"line": 11,
"column": 1
},
"valueStart": 311,
"valueEnd": 316,
"lineStart": 286,
"lineEnd": 317
}
]
},
{
"ordinal": 2,
"ordinalText": "2",
"title": "Offer a next action",
"status": "in progress",
"id": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15",
"location": {
"offset": 392,
"line": 15,
"column": 1
},
"headingEnd": 428,
"end": 627,
"body": "**Status:** in progress\n**Milestone ID:** 8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15\n**Acceptance criteria:** AC-02, AC-03\n\nAdd the clear-filters action, then check it with a keyboard and a screen reader.\n",
"statusMarkers": [
{
"value": "in progress",
"location": {
"offset": 428,
"line": 16,
"column": 1
},
"valueStart": 440,
"valueEnd": 451,
"lineStart": 428,
"lineEnd": 452
}
],
"idMarkers": [
{
"value": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15",
"location": {
"offset": 452,
"line": 17,
"column": 1
},
"valueStart": 470,
"valueEnd": 506,
"lineStart": 452,
"lineEnd": 507
}
],
"criteria": [
"AC-02",
"AC-03"
],
"criteriaMarkers": [
{
"value": "AC-02, AC-03",
"location": {
"offset": 507,
"line": 18,
"column": 1
},
"valueStart": 532,
"valueEnd": 544,
"lineStart": 507,
"lineEnd": 545
}
]
}
],
"overview": [],
"overviewPresent": false,
"diagnostics": []
}
},
"criteria": {
"protocol": "0.2",
"criteria": [
{
"id": "AC-01",
"text": "Empty search results explain why nothing matched.",
"location": {
"offset": 456,
"line": 24,
"column": 3
},
"milestones": [
{
"ordinal": 1,
"id": "3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34"
}
],
"progress": "done"
},
{
"id": "AC-02",
"text": "The empty state offers one next action, such as clearing the active filters.",
"location": {
"offset": 515,
"line": 25,
"column": 3
},
"milestones": [
{
"ordinal": 2,
"id": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15"
}
],
"progress": "in progress"
},
{
"id": "AC-03",
"text": "The explanation and the action work with a keyboard and a screen reader.",
"location": {
"offset": 601,
"line": 26,
"column": 3
},
"milestones": [
{
"ordinal": 2,
"id": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15"
}
],
"progress": "in progress"
},
{
"id": "AC-04",
"text": "Search suggests spelling corrections.",
"location": {
"offset": 683,
"line": 27,
"column": 3
},
"milestones": [],
"progress": null
}
],
"diagnostics": [
{
"document": "plan",
"code": "UNASSIGNED_CRITERION",
"message": "No milestone covers this criterion.",
"severity": "error",
"location": {
"offset": 683,
"line": 27,
"column": 3
}
}
]
},
"diagnostics": [
{
"document": "plan",
"code": "UNASSIGNED_CRITERION",
"message": "No milestone covers this criterion.",
"severity": "error",
"location": {
"offset": 683,
"line": 27,
"column": 3
}
}
]
}Mutation and check results
{
"directory": "jg-plans/search-feedback",
"document": "plan",
"changed": true,
"sha256": "99cbfac6e98dcfe04052fef6a5031e586464f8be578d0a491691158b8c5beb4a",
"diagnostics": [],
"assignedMilestoneIds": []
}{
"conforms": false,
"diagnostics": [
{
"document": "plan",
"code": "UNASSIGNED_CRITERION",
"message": "No milestone covers this criterion.",
"severity": "error",
"location": {
"offset": 683,
"line": 27,
"column": 3
}
}
]
}Consumer-derived summary
{
"revision": 2,
"status": "in progress",
"milestones": [
{
"id": "3c7a2f1e-9b4d-4e8a-a6f2-1d5c7e9b0a34",
"title": "Explain empty results",
"status": "done"
},
{
"id": "8e1d6b2a-4f7c-4a3e-b9d1-6c2e8f4a7b15",
"title": "Offer a next action",
"status": "in progress"
}
],
"criteria": [
{
"id": "AC-01",
"milestones": [
1
],
"progress": "done"
},
{
"id": "AC-02",
"milestones": [
2
],
"progress": "in progress"
},
{
"id": "AC-03",
"milestones": [
2
],
"progress": "in progress"
},
{
"id": "AC-04",
"milestones": [],
"progress": null
}
],
"binding": "stale",
"check": {
"conforms": false,
"findings": [
{
"code": "UNASSIGNED_CRITERION",
"criterion": "AC-04",
"line": 27
}
]
}
}Public API call · excerpt
if (current.plan.state === 'readable') {
await writePlanDocument(directory, {
document: 'plan',
expectedSha256: current.plan.sha256,
body: revisedBody,
set: { revision: 2 },
});
}
const revised = await inspectPlan(directory);
if (revised.plan.state === 'readable' && revised.milestones.state === 'readable') {
const check = checkPlan({
plan: revised.plan.value.source,
milestones: revised.milestones.value.source,
});
}Complete reference-library results. Runtime directory and document-path fields are shown as jg-plans/search-feedback and its document paths. Source, body, metadata, diagnostics, IDs, and hashes are unchanged. Summaries, criterion progress, and the skills binding are consumer-derived views.
From a file to your product
Build the tool
you have in mind.
jg-plans reads the documents; your app decides what to show, from progress to agent context.
See the integration guideAgent context
Give an agent the selected plan and the work still ahead.
Custom automation
Connect JSON observations to your own project rules.
YOUR DASHBOARD / EXAMPLE VIEW
Search feedback
Milestones
- 1Explain empty resultsdone
- 2Offer a next actionin progress
Acceptance criteria
AC-01done
Empty search results explain why nothing matched.
Delivered by milestone 1
AC-02in progress
The empty state offers one next action, such as clearing the active filters.
Delivered by milestone 2
AC-03in progress
The explanation and the action work with a keyboard and a screen reader.
Delivered by milestone 2
Parsed from the published criteria example at build time.
Checked before work starts
Catch mistakes on their line.
check names every protocol mistake in a plan and its milestones.
- 1---
- 2id: 1792612980_export-reports-criteria
- 3created_at: 2026-10-21T20:03:00Z
- 4updated_at: 2026-10-21T20:03:00Z
- 5revision: 1
- 6status: blockedINVALID_STATUS
- 7protocol: "0.2"
- 8---
- 9
- 10# Export reports
- 11
- 12Let people download a report as CSV.
- 13
- 14## Acceptance criteria
- 15
- 16- AC-01: The export contains every visible column.
- 17- AC-01: The export keeps the current sort order.DUPLICATE_CRITERION
- 18- AC-2: The file name includes the report date.INVALID_CRITERION_ID
- 19- AC-03: Large exports finish without timing out.UNASSIGNED_CRITERION
- 1## Milestone 1: Build the export
- 2**Status:** pending
- 3**Milestone ID:** 6f2b9c4d-1e7a-4d3b-8c5f-2a9e7d1b4c63
- 4**Acceptance criteria:** AC-01, AC-07UNKNOWN_CRITERION
- 5
- 6Generate the CSV from the visible table.
Actual check output for the published criteria-invalid example.
A public API. A local CLI.
Read it. Inspect it.
Edit it precisely.
1 You read the planComplete2 Someone revises itCompletesha256 99cbfac6e98d…
3 Your stale write is refusedErrorJG_PLANS_SOURCE_CHANGED
npm install jg-plans@0.5.1
Node.js 24.15.0+ · Protocol 0.2 is provisional.
import { inspectPlan, updatePlan } from 'jg-plans';
const current = await inspectPlan('./coding-agent-planning/example');
if (current.plan.state === 'readable') {
await updatePlan(current.directory, {
document: 'plan',
expectedSha256: current.plan.sha256,
set: { status: 'in progress' },
});
}Three pieces. Clear responsibilities.
A format to share.
A workflow to make your own.
01 / DOCUMENT FORMAT
your-project/
plan.md
milestones.md optional
The protocol
Shared fields, markers and criteria; ordinary Markdown for the rest.
Explore the format →02 / DOCUMENT OPERATIONS
parseinspectcheckupdateThe library & CLI
Structured observations and guarded edits that keep unrelated text.
Meet the tools →03 / APPLICATION BEHAVIOR
ApprovalsExecutionCoordinationRetentionYour implementation
Your project owns approvals, execution and every other decision.
Build an integration →
As much structure as the work needs
Start small. Stay in control.
plan.md
One file is enough.
Identity, revision and status above ordinary Markdown.
+ milestones.md
Break it down when useful.
Numbered milestones name the criteria they deliver.
edit · move · delete
Your files remain yours.
Custom fields survive edits, and deleted plans stay deleted.
- your-project
- jg-plans
- 1792612920_search-feedback-criteria
- plan.md
- milestones.md