{
  "markdown": "# Sift\n\nSift stores tickets as Markdown files with YAML front-matter. This specification ships\nas `.ai/sift/README.md` so each repository can work offline with its installed convention.\nState lives in files. No database, daemon or CLI installation is required.\n\n## Configuration\n\nSet the permanent prefix in `.ai/sift/config/config.yaml`:\n\n```yaml\nprefix: ABCD      # uppercase; stable for the life of the repository\n```\n\n`MILESTONES.md` names and orders milestones. `<PREFIX>` and `<milestone>` below stand for\nthese configured values. Changing a prefix requires rewriting every ticket ID, filename and\nreference with the config change.\n\n## Directory layout\n\n```\n.ai/sift/\n├── .gitignore                 ← * and !.gitignore; delete to track the tree\n├── README.md                  ← installed specification\n├── MILESTONES.md              ← milestone definitions\n├── .id-sequence/              ← reservation marks; created on first allocation\n├── RUNLOG.md                  ← diagnostic log; created on first dispatch\n├── config/\n│   └── config.yaml            ← ticket prefix\n├── scripts/\n│   └── sift.sh                ← local operations\n├── schemas/\n│   ├── sift-common.xsd\n│   ├── bug-ticket.xsd\n│   ├── feature-ticket.xsd\n│   ├── task-ticket.xsd\n│   └── bug-ticket.example.xml ← optional worked draft\n├── open/\n│   └── <milestone>/\n│       └── <category>/        ← bug | hardening | feature | test | docs | dx | release\n│           └── <PREFIX>-0001--short-slug.md\n└── archive/\n    └── <milestone>/<category>/<PREFIX>-0001--short-slug.md\n```\n\nEach file is one ticket. `open/` holds actionable work, including blocked and in-progress\ntickets. `archive/` holds terminal tickets. Milestone and category folders match front-matter\nand exist only while they hold a ticket; the two buckets themselves always exist.\nThe tree is ignored by default; search it with `find` and `command grep` or no-ignore flags.\n\n## File naming\n\nUse `<PREFIX>-<NNNN>--<kebab-slug>.md`. IDs are immutable, globally unique across both buckets,\nand zero-padded to at least four digits. Slugs may change.\n\nReserve IDs before writing with `scripts/sift.sh reserve` or a skill's `reserve-ids.sh`.\nAll allocators share `.id-sequence/.lock/` and the highest reserved number in\n`.id-sequence/<PREFIX>`. They advance beyond both the mark and existing filenames, then\npublish the mark before returning IDs. Keep unused numbers as gaps. Never lower or delete\nmarks; include them in backups. Numbers widen past 9999, up to 15 digits.\n\nExit 3 means the lock is busy or unavailable. Retry after its owner finishes; never fall\nback to calculating an ID. After a crash, stop callers and confirm no owner remains before\nremoving the lock's scratch files and empty directory. Restore damaged marks from a backup\nthat includes outstanding reservations.\n\n## Front-matter schema\n\nEvery ticket starts with YAML front-matter. Required keys are marked ✱.\n\n```yaml\n---\nid: <PREFIX>-0042      # ✱ matches the filename prefix\ntitle: Short imperative summary            # ✱\nstatus: open           # ✱ open | in-progress | blocked | done | wontfix | superseded\ntype: bug              # ✱ bug | hardening | feature | test | docs | dx | release\nmilestone: <milestone> # ✱ from MILESTONES.md; must match the folder it lives in\npriority: p2           # ✱ p1 critical | p2 high | p3 normal | p4 someday\neffort: m              # ✱ s | m | l | xl (honest guess, revise freely)\ncreated: 2026-08-05    # ✱ YYYY-MM-DD\nupdated: 2026-08-05    # ✱ bump on every meaningful edit\nlabels: [api, caching] # free-form kebab tags\ncluster: whole-token-ids  # optional kebab name of a root cause shared with other tickets\ndepends_on: []         # list of ticket IDs that must land first, e.g. [<PREFIX>-0041]\nwave: 1                # required positive integer while open; see rule 9\nresolution: \"\"         # required non-empty when archived: one line on how it ended\nsource: \"\"             # where the ticket came from (session, issue URL, review)\n---\n```\n\n`open | in-progress | blocked` belong in `open/` and require a positive wave.\n`done | wontfix | superseded` belong in `archive/` and require a non-empty resolution.\n\n## Dispatch groups and the cluster key\n\n`cluster` optionally names a shared root cause using lowercase letters/digits and single\ninternal hyphens. Missing or malformed values give no hint and never fail a run.\n\nMerge findings only when one Direction applies unchanged at every site. Separate tickets\nmay share a cluster when one worker can orient to their root cause together. Keep each\nticket's Direction and archive operation. Sequence overlapping product writes.\n\n`next-ticket.sh --group` selects by wave, then priority. It excludes archived and blocked\ntickets, then groups consecutive dispatchable tickets sharing the lead's cluster. Stop at\na non-member, wave boundary or the first ticket that would exceed either limit: **4 tickets**\nand **8 effort weight**. Cluster never changes priority, authorization or ticket state.\n\n| effort | xs | s | m | l | xl |\n|---|---|---|---|---|---|\n| weight | 1 | 2 | 3 | 5 | 8 |\n\nThe schema allows s, m, l and xl. The helper accepts xs and defaults unknown effort to m.\n\n## Ticket body\n\nUse these sections in order. Omit only empty optional sections.\n\n```markdown\n# <title>\n\n## Problem\nWhat is wrong or missing, and why. Two to six sentences.\n\n## Evidence\nCitations supporting the claim.\n\n## Direction\nApproach and constraints.\n\n## Acceptance criteria\n- [ ] Checkable condition for completion.\n```\n\nFor bugs, Expected behaviour and Evidence are required; Steps to reproduce is optional:\n\n```markdown\n## Problem\n## Expected behaviour\n## Steps to reproduce        ← optional\n## Evidence\n## Direction\n## Acceptance criteria\n```\n\nFor features, Problem states motivation. Alternatives considered is optional:\n\n```markdown\n## Problem\n## Evidence\n## Direction\n## Alternatives considered\n## Acceptance criteria\n```\n\nOther types use the four canonical sections.\n\n## Drafting a ticket\n\nRead each needed schema once as a checklist, then write Markdown directly. Use bug-ticket\nfor bugs, feature-ticket for features and task-ticket for other types. XML drafts and\n`xmllint` validation are optional; see `schemas/bug-ticket.example.xml`. Never store XML tickets or require that binary.\n\nCheck two rules outside XSD 1.0: terminal tickets need a non-empty resolution, and milestone\nmust match both MILESTONES.md and the folder.\n\n## Mapping to a remote tracker\n\nFor an authorized mirror, put the issue URL in `source:`. Keep front-matter authoritative.\n\n| Sift field | Remote label |\n|---|---|\n| type bug / feature / other | category::bug / category::feature / category::task |\n| priority p1 / p2 / p3 / p4 | priority::critical / priority::major / priority::normal / priority::minor |\n| status | state::*; discard remote states with no Sift equivalent |\n| archived resolution | why::* and closing comment |\n| labels | plain kebab topic labels; never duplicate type, priority or status |\n\n## Run log\n\nDrain alone appends RUNLOG.md. Never edit it by hand or use it as ticket state.\nSix columns, with `-` for unused cells:\n\n```markdown\n# Run log\n\nAppend-only. One row per drain event; rows are never rewritten.\n\n| event | ticket | phase | utc | epoch | status |\n|---|---|---|---|---|---|\n| dispatch | <PREFIX>-0025 | - | 2026-08-10T09:15:04Z | 1786353304 | - |\n| dispatch | <PREFIX>-0031 | - | 2026-08-10T09:15:04Z | 1786353304 | - |\n| phase | - | orient | 2026-08-10T09:15:41Z | 1786353341 | - |\n| phase | - | implement | 2026-08-10T09:22:10Z | 1786353730 | - |\n| phase | - | verify | 2026-08-10T09:34:57Z | 1786354497 | - |\n| phase | - | bookkeep | 2026-08-10T09:39:02Z | 1786354742 | - |\n| return | <PREFIX>-0025 | - | 2026-08-10T09:41:12Z | 1786354872 | done |\n| return | <PREFIX>-0031 | - | 2026-08-10T09:41:12Z | 1786354872 | done |\n```\n\nEach dispatch or return call writes one row per ticket with a shared timestamp. Each phase\nwrites one ticketless row. Readers group dispatches by epoch and calculate with that integer,\nnot UTC text. Writers use `date -u +%Y-%m-%dT%H:%M:%SZ` and `date +%s`.\n\n## Rules for agents\n\n1. **Read this file before creating or moving tickets.** Read the applicable sections:\n   Rules for agents, Front-matter schema, Ticket body, and Drafting a ticket for creation;\n   the relevant operation for allocation or moves. Follow them exactly.\n2. **Never renumber, reuse, or delete a ticket ID.** Wrong ticket? Archive it with\n   `status: wontfix` and a `resolution`. Files are deleted only by the human owner.\n3. **Front-matter is the source of truth**; folders are an index. When you `mv` a\n   ticket, update `milestone`/`status` front-matter in the same change, and vice versa.\n   Delete the folders the move left empty, as rule 4 says for archiving.\n4. **Archiving = edit + mv + prune.** Set `status`, `resolution`, `updated`, then `mv` the\n   file to the mirrored path under `archive/` (`mkdir -p` the target first). Delete the\n   category and milestone folders the move left empty. No empty folder stays under `open/`\n   or `archive/`; never delete the buckets themselves.\n5. **One problem per ticket, and evidence-based.** Claims about code cite `file:line`.\n6. **Bump `updated`** whenever you change anything meaningful.\n7. Cross-reference tickets inline as `<PREFIX>-XXXX`, as plain text.\n8. New milestone or category folders are allowed, but document new milestones in\n   `MILESTONES.md` in the same change. Categories are a closed set; propose additions\n   by editing this README.\n9. **Give every open ticket a `wave: <n>`, set once at drafting time.** Choose a wave no\n   earlier than the latest wave among tickets it `depends_on`. Archiving does not touch it:\n   an archived ticket keeps whatever wave it was drafted with, and the key is never required\n   or edited after resolution. Run `scripts/sift.sh consistency`. An open\n   ticket with no `wave:`, or a `depends_on` ID pointing at nothing, is a convention\n   violation.\n10. **Use the body template for the ticket's `type`.** A `bug` states its expected\n    behaviour and cites `file:line`; a `feature` states its motivation. Draft against\n    `schemas/` when writing a new ticket — but never make anything depend on `xmllint`\n    being installed.\n\n## Operations cookbook (terminal)\n\nRun from the project root. The installed script reads the prefix and handles errors;\nno shell setup or pasted implementation is needed. Use `SIFT_ROOT` to select another root\nand `SIFT_PREFIX` to override the prefix. Arguments shown in capitals are supplied values.\n\n```sh\nbash .ai/sift/scripts/sift.sh --help\n```\n\n| Command after `bash .ai/sift/scripts/sift.sh` | Operation |\n|---|---|\n| `list` | Open ticket paths |\n| `reserve [COUNT]` | Reserve IDs, default one |\n| `triage [MILESTONE]` | Titles for a milestone, default first and critical ticket paths |\n| `counts` | Open counts per milestone |\n| `find ID` | Find an exact ID in either bucket |\n| `search TERM` | Case-insensitive ticket text search |\n| `labels` / `label-counts` / `label LABEL` | Label inventory, counts or matching tickets |\n| `dependents ID` | References to an ID, excluding its own file |\n| `next` | Unblocked critical tickets; drain uses its wave graph instead |\n| `move ID MILESTONE` | Move an open ticket and update its milestone; delete emptied folders |\n| `archive ID STATUS RESOLUTION` | Set terminal status, resolution and updated; move to archive; delete emptied folders |\n| `consistency` | Missing waves and unresolved dependencies |\n| `required` | Missing required keys |\n| `resolutions` | Terminal tickets without a resolution |\n| `bugs` / `features` | Missing Expected behaviour or Direction |\n| `folders` | Folder/milestone disagreement |\n| `validate-draft SCHEMA FILE` | Validate XML if xmllint is installed |\n\nQuote arguments containing spaces. Audits print findings for review; they do not repair\nfiles or signal findings through the exit code. `required` also prints key headers.\nMove and archive reject missing or ambiguous IDs before writing. A move whose ticket lacks\nmilestone front-matter reports the moved path for manual repair.\n\n## Updating installed copies\n\nUpdate the Sift skills, then run sift-init to add missing shipped files. It preserves existing\nfiles and reports drift. Review local annotations before applying its resolved refresh\ncommands. Only the specification, schemas and operation script are shipped assets:\n\n```sh\ncp \"$SKILL/assets/README.md\" .ai/sift/README.md\ncp \"$SKILL\"/assets/schemas/*.xsd \"$SKILL\"/assets/schemas/*.xml .ai/sift/schemas/\ncp \"$SKILL/assets/scripts/sift.sh\" .ai/sift/scripts/sift.sh\n```\n\n`SKILL` is the installed sift-init directory. Refresh never changes tickets, milestones,\nconfig, logs or reservation marks. Orphan schemas remain until the owner removes them.\n\nMigration is additive: sift-init installs `scripts/sift.sh`; no ticket path or key changes.\nFolders emptied before the prune rule existed stay until removed once:\n\n```sh\nfind .ai/sift/open .ai/sift/archive -depth -mindepth 1 -type d | while read -r d; do\n  rmdir \"$d\" 2>/dev/null || :\ndone\n```\n\nReplace pasted cookbook recipes with the commands above. When upgrading from read-only ID\nallocation, stop old writers and materialize their outstanding IDs as tickets first. Update\nPrime and Drain together before restarting; the first reservation seeds its mark from both\nbuckets.\n",
  "bytes": 13331,
  "sha": "d15688eb84384d02acc3beb87d5f2016331ece6c5b46f25fa2995adb6e7402b7",
  "repo_slug": "e0ipso/sift",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/okf_e0ipso_sift_ai_kenkeep_nodes_index_md_6279fbbe/readme"
}