create-pathfinder 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -76,3 +76,5 @@ An adapter carries the canonical skill's frontmatter and a pointer to it, and no
76
76
  - `reflect` — review completed work, and the reflection itself, and propose reusable workflow improvements for human approval
77
77
  - `handoff` — preserve useful state between sessions or tools
78
78
  - `skillsmith` — teach and create small local skills
79
+ - `setup-tracker` — configure an optional external work tracker
80
+ - `sync-tracker` — publish approved feature specs to the configured tracker, one-way and idempotently
package/README.md CHANGED
@@ -27,7 +27,7 @@ The installer copies five things into your repository and nothing else:
27
27
  | --- | --- |
28
28
  | `AGENTS.md`, `CLAUDE.md` | Entry files that tell an agent how to work in the project |
29
29
  | `context/` | Project truth — overview, standards, interaction rules, current feature |
30
- | `skills/` | Twenty skills covering discovery, specs, delivery, debugging, review, and learning |
30
+ | `skills/` | Skills covering discovery, specs, delivery, debugging, review, learning, and optional work tracking |
31
31
  | `templates/` | Starting points the project copies when it needs them |
32
32
 
33
33
  On request it also writes one thing it does not copy:
@@ -20,6 +20,7 @@ Follow the project's documented policy. Unless explicitly pre-approved, ask befo
20
20
  - Git history rewriting
21
21
  - commits, merges, releases, or deployments
22
22
  - adopting prototype code into production
23
+ - writes that leave this repository, such as creating or editing items on a shared work tracker — writing files inside the repository is an ordinary file edit and is not covered
23
24
 
24
25
  ## Git and Delivery
25
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pathfinder",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Install the Pathfinder AI-assisted, human-in-the-loop workflow kit into a Git repository.",
5
5
  "keywords": [
6
6
  "pathfinder",
@@ -11,7 +11,8 @@ description: Close an accepted feature through final verification, records, deli
11
11
  4. Follow the approved commit, PR, merge, changelog, versioning, release, and deployment policy—requesting approval where required. After the merge, verify the merged mainline and clean up the merged branch as that policy requires.
12
12
  5. Append the durable outcome to `context/history.md` while completing the feature, not afterwards. If the feature was merged without this skill running, still write the entry and record that it was written after the fact.
13
13
  6. Mark/reset `context/current-feature.md` and identify the next action.
14
- 7. Offer or invoke `learn-feature` when learning is enabled.
15
- 8. Produce a compact completion summary.
14
+ 7. If `context/tracker.md` exists, reconcile this feature's tracked item with `sync-tracker` after the merge.
15
+ 8. Offer or invoke `learn-feature` when learning is enabled.
16
+ 9. Produce a compact completion summary.
16
17
 
17
18
  Do not claim completion when checks failed, evidence is missing, or the feature remains unaccepted.
@@ -12,6 +12,7 @@ description: Prepare one feature for implementation by checking readiness, conte
12
12
  5. Assess whether the feature fits a focused LLM context window.
13
13
  6. Split or revise it before implementation if the context is too broad.
14
14
  7. Populate `context/current-feature.md` with the feature, first delivery chunk, context boundary, assumptions, Git state, definition of done, and out-of-scope work.
15
- 8. Present a short readiness summary.
15
+ 8. If `context/tracker.md` exists, note this feature's tracked item, or that it has none yet. The lookup is best-effort: an unreachable tracker is reported and never blocks loading.
16
+ 9. Present a short readiness summary.
16
17
 
17
18
  Do not implement, create Git history, or resolve `TBD` decisions silently.
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: setup-tracker
3
+ description: Configure an optional external work tracker by interviewing the human and writing context/tracker.md from the shipped template.
4
+ ---
5
+
6
+ # Setup Tracker
7
+
8
+ Use this skill to describe, in prose, which tracker a project uses and how
9
+ Pathfinder's work items project onto it. The result is `context/tracker.md`.
10
+
11
+ Work Tracking is **optional**. A project that never runs this skill is
12
+ unaffected: no file, no prompt, no behaviour change anywhere. Do not run this
13
+ skill unasked, and do not propose it as a missing step.
14
+
15
+ This skill configures. It never contacts a tracker.
16
+
17
+ ## Rules
18
+
19
+ - Do not write `context/tracker.md` without human approval.
20
+ - Do not add code, dependencies, or an adapter for any tracker.
21
+ - Do not invent a taxonomy the team did not ask for.
22
+ - Keep the model section backend-neutral; vendor vocabulary belongs only under
23
+ the projection heading.
24
+ - Never remove the work-item marker block. It is the one machine-stable token in
25
+ a prose contract, and identity depends on it.
26
+
27
+ ## Interview
28
+
29
+ Ask progressively, in small groups. Ask only what the config cannot be written
30
+ without:
31
+
32
+ 1. Which tracker, and where it lives.
33
+ 2. How an agent reaches it — a CLI, a path, or a described manual step.
34
+ 3. Which tag namespaces the project actually uses. `area`, `type`, `priority`,
35
+ and `agent` are available; a project needing none of them says so.
36
+ 4. What the tracker already carries. **List its existing labels, fields, or
37
+ conventions before proposing any tag value.** If something already means the
38
+ same thing, reuse it and record the mapping rather than creating a
39
+ near-duplicate.
40
+ 5. Anything about the projection that the shipped starting points do not cover.
41
+
42
+ ## Process
43
+
44
+ 1. Check whether `context/tracker.md` already exists. If it does, read it, and
45
+ treat this run as a revision — show what would change and change nothing
46
+ else. Never overwrite a working config wholesale.
47
+ 2. Read `templates/tracker.template.md`.
48
+ 3. Run the interview.
49
+ 4. Choose the projection:
50
+ - **GitHub Issues via `gh`** and **local Markdown files** ship as starting
51
+ points, both proven.
52
+ - Any other tracker is supported by the human describing it in prose, using a
53
+ shipped projection as the shape to follow. **That is the mechanism, not a
54
+ gap** — do not report an unsupported tracker as a blocker.
55
+ 5. Fill the placeholders and keep **one** projection block. Delete, in the
56
+ written config:
57
+ - the template's guidance blockquote at the top;
58
+ - the projection block you did not keep;
59
+ - the `pathfinder:model-start` and `pathfinder:projection-boundary` markers;
60
+ - the italic *"Keep this block for…"* line under the projection you kept — it
61
+ is an instruction to you, not content for the project to read.
62
+
63
+ **The model — everything between the two markers — is kept byte-for-byte.**
64
+ The tracker-identity paragraph above it is the one part you rewrite, because
65
+ it names the tracker and how to reach it.
66
+ 6. Present the proposed file and request approval.
67
+ 7. Write `context/tracker.md` only after the human approves.
68
+ 8. Report what was configured, and say plainly that nothing has been published.
69
+
70
+ ## Stop Condition
71
+
72
+ Stop once the config is written. Publishing to a tracker is `sync-tracker`, not
73
+ this skill. If `sync-tracker` is not installed, say so rather than publishing by
74
+ hand.
@@ -23,3 +23,4 @@ Restate goal, chunk, files/areas, context, risks, assumptions, verification, exc
23
23
  - Stop on conflicts between spec, durable context, and repository reality.
24
24
  - Do not add dependencies, expand scope, adopt prototype code, commit, merge, or deploy without the documented approval.
25
25
  - Do not hide multiple features inside one delivery chunk.
26
+ - Do not publish to a work tracker, even when `context/tracker.md` exists — the repository is canonical and a chunk boundary is not a tracker event.
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: sync-tracker
3
+ description: Publish approved feature specs to the configured work tracker, one-way and idempotently, writing nothing when nothing has changed.
4
+ ---
5
+
6
+ # Sync Tracker
7
+
8
+ Use this skill to project the project's approved feature specs onto the tracker
9
+ described in `context/tracker.md`.
10
+
11
+ The repository is canonical. This is a **one-way projection** of work that
12
+ already exists. Nothing here reads tracker state back into a spec,
13
+ `context/current-feature.md`, or `context/history.md`.
14
+
15
+ ## The off switch comes first
16
+
17
+ **If `context/tracker.md` does not exist, do nothing at all.** Report that work
18
+ tracking is not configured for this project, and stop.
19
+
20
+ Do not create the config, do not propose configuring one, and do not describe
21
+ its absence as a gap. Work Tracking is optional, and a project without it is
22
+ behaving correctly.
23
+
24
+ ## The config is the contract
25
+
26
+ Read `context/tracker.md` in full and follow its prose. It states the model, the
27
+ projection, and the publishing rules for this project.
28
+
29
+ There is **no adapter code, no vendor branch, and no required tool** beyond what
30
+ the config itself names. If the config describes a tracker this skill has never
31
+ heard of, that is the design working, not a blocker.
32
+
33
+ Where the config is silent on something a run needs, **ask the human**. Do not
34
+ fill the gap with a convention of your own — the next run would fill it
35
+ differently, and every item would look modified.
36
+
37
+ ## What gets published
38
+
39
+ **Approved feature specs only**, from `to-specs` onward — one work item per spec.
40
+ Never debate notes, kickstart output, or prototypes. The one exception is a
41
+ prototype that gates a decision, published as a single item phrased as the
42
+ decision it resolves, never as a deliverable.
43
+
44
+ **Do not decompose a feature.** Choosing units of execution inside a feature is a
45
+ judgement about how work will be shared out, and it belongs to a human or to a
46
+ skill written for it. Publish the feature.
47
+
48
+ ## Process
49
+
50
+ 1. Check for `context/tracker.md`. If it is absent, report and stop.
51
+ 2. Read the config. Read the approved specs. Read nothing else.
52
+ 3. Build the work items: key, kind, title, body, blocked-by edges, tags, and
53
+ chunks, derived and composed exactly as the config's model section says. Every
54
+ field comes from the spec — **tags only from its optional `## Tags` section,
55
+ never inferred.** A spec without one has no tags, which is correct and common.
56
+ 4. Order them by dependency, blockers first. If the edges contain a cycle,
57
+ report it and publish nothing — a guessed order is a wrong order that looks
58
+ fine.
59
+ 5. **Ask the human before the first write that leaves this repository.** See
60
+ below.
61
+ 6. Publish, following the config's *Publishing, and re-publishing* section
62
+ exactly: index existing items by key, create what is missing, compare
63
+ normalized, and leave an unchanged item completely alone.
64
+ 7. Report what was created, what was edited, and what was left alone.
65
+
66
+ ## The approval gate
67
+
68
+ **Ask before the first write to a tracker outside this repository in a session.**
69
+ Creating items on a shared board is outward-facing and is not covered by ordinary
70
+ file-edit approval. One approval covers the run; do not ask per item.
71
+
72
+ **A projection onto files inside this repository is not gated.** It is an
73
+ ordinary file edit and reaches nothing outside the repository. The gate is about
74
+ leaving the repository, not about writing.
75
+
76
+ ## Idempotency is the whole feature
77
+
78
+ A second run over unchanged specs must **create nothing, change nothing, and
79
+ issue zero writes**. Not writes that happen to be no-ops — no writes.
80
+
81
+ - **Compare normalized, never raw bytes.** A tracker is not obliged to hand a
82
+ body back exactly as it was sent, and APIs differ in whether they adjust
83
+ trailing whitespace. Where that happens, a naive byte comparison reports every
84
+ item as changed on every run and rewrites all of them forever, which looks like
85
+ working sync and is not. Strip trailing whitespace from each line and collapse
86
+ trailing blank lines at the end, **on both sides**, before comparing. It costs
87
+ nothing when the round-trip is exact.
88
+ - **Compare tag sets as sets**, not as ordered lists. Application order is not
89
+ preserved.
90
+ - Never close, reopen, delete, or recreate an item. Edit in place.
91
+ - Never touch an item whose key is absent from the current set. It belongs to
92
+ work outside this run.
93
+ - Match on the key alone, never on the title.
94
+
95
+ **Report writes, not final state.** "The tracker looks right" is satisfied by a
96
+ run that rewrote every item, which is the specific failure this skill exists to
97
+ avoid. `3 items, 0 changes` is the expected result of a second run and the most
98
+ important line of output.
99
+
100
+ ## Rules
101
+
102
+ - Do not add code, dependencies, or an adapter for any tracker.
103
+ - Do not branch on `kind`.
104
+ - Do not infer tags, and do not apply a value the config's mapping table does not
105
+ carry. A spec naming an unmapped tag is a question for the human, not a
106
+ judgement call.
107
+ - Do not drop an edge that resolves outside the published set, and do not invent
108
+ an item for it. Resolve it against the tracker as a whole: render the item's
109
+ identifier if the key already has one, and name the key as untracked only when
110
+ it genuinely has none.
111
+ - Do not read anything back. A ticked checkbox means nothing to Pathfinder and
112
+ never advances any state.
113
+ - Do not close, reopen, or delete anything, ever.
114
+ - Stop and report when the config, the specs, and the tracker disagree.
115
+
116
+ ## Stop Condition
117
+
118
+ Stop once the run is reported. Configuring a tracker is `setup-tracker`, not this
119
+ skill. If a spec set is not ready to publish, say so rather than publishing part
120
+ of it.
@@ -36,8 +36,12 @@ Create only the coherent MVP roadmap in `context/features/`, using `templates/fe
36
36
 
37
37
  Each spec must include Context Boundary, Delivery Chunks, and Learning Targets.
38
38
 
39
+ Write a `## Tags` section only when the project has configured work tracking and its `context/tracker.md` defines tag namespaces. Use the values that config already lists, never invented ones. No tags is a valid and common answer — omit the section rather than guessing.
40
+
39
41
  After creation, summarize file, outcome, dependency, visible/verifiable win, context risk, and recommended first feature.
40
42
 
43
+ If `context/tracker.md` exists, offer to publish the new specs with `sync-tracker`. If it does not, say nothing about tracking.
44
+
41
45
  ## Rules
42
46
 
43
47
  - Do not implement or install packages.
package/src/detect.mjs CHANGED
@@ -119,7 +119,7 @@ function detectTool(tool, { cwd, home, env, platform }) {
119
119
  * Decided by counting skill directories rather than by testing for `CLAUDE.md`,
120
120
  * which any agent-assisted project may have written for its own reasons.
121
121
  * A `skills/<name>/SKILL.md` is a far more specific signature, and the count is
122
- * worth having on its own — it is what makes "already installed (20 skills)"
122
+ * worth having on its own — it is what makes "already installed (N skills)"
123
123
  * checkable by the person reading it.
124
124
  */
125
125
  function detectPathfinder(cwd) {
@@ -4,7 +4,7 @@
4
4
  * This used to be one hardcoded string naming a file path, and that was right
5
5
  * for exactly as long as Pathfinder configured nothing. Once a run can generate
6
6
  * native adapters, the path form is no longer the best answer for someone who
7
- * just watched twenty skills be installed into their harness — it is the answer
7
+ * just watched the whole kit be installed into their harness — it is the answer
8
8
  * for someone whose tool cannot discover them.
9
9
  *
10
10
  * A pure function of the selection, deliberately: no filesystem, no detection,
@@ -22,6 +22,12 @@ State one user-visible or system-verifiable outcome.
22
22
 
23
23
  - Earlier feature, system, decision, prototype, or `None`.
24
24
 
25
+ ## Tags
26
+
27
+ - Optional. `namespace:value`, e.g. `area:cli`, `type:infra`, `agent:suitable`.
28
+ - Omit this section entirely, or write `None`, when the project does not use tags. Most do not.
29
+ - Only meaningful when the project has configured work tracking; nothing else reads them.
30
+
25
31
  ## Requirements
26
32
 
27
33
  - Requirement 1
@@ -0,0 +1,359 @@
1
+ # Tracker
2
+
3
+ > Starting point for `context/tracker.md`. `setup-tracker` fills the placeholders
4
+ > and keeps **one** projection block, then deletes this blockquote.
5
+ >
6
+ > **The file's absence is the off switch.** Work Tracking is optional. A project
7
+ > with no `context/tracker.md` behaves exactly as it always has, and nothing in
8
+ > Pathfinder prompts for one. Do not ship this template's output unconfigured.
9
+ >
10
+ > **The backend-neutral model is everything between the two markers below** —
11
+ > from `pathfinder:model-start` down to `pathfinder:projection-boundary` — and it
12
+ > **must be byte-identical whichever projection is chosen.** Choosing a backend
13
+ > cuts below the projection boundary and nowhere else. Nothing inside the model
14
+ > may be *defined* in a backend's terms — an "issue number", a "label", a file
15
+ > path. Naming one in order to rule it out is not the same thing and is correct:
16
+ > "edges name keys, never tracker issue numbers" is the neutrality rule being
17
+ > stated, not a leak. The test is whether changing backend would change the
18
+ > sentence.
19
+ >
20
+ > The two paragraphs above the model marker are this project's own — the tracker
21
+ > it uses, and the rule that the repository wins. They are *not* claimed to be
22
+ > byte-identical across backends: the first one names the tracker and how an
23
+ > agent reaches it, so it necessarily differs.
24
+
25
+ This project tracks work in `[tracker name]`, at `[where it lives]`, reached by
26
+ `[how to reach it]`.
27
+
28
+ The repository is canonical. This tracker is a **one-way projection** of work
29
+ that already exists in `context/features/`. Never read status back out of the
30
+ tracker and into a spec. If the two disagree, the repository is right and the
31
+ tracker is stale.
32
+
33
+ <!-- pathfinder:model-start
34
+ Everything from here to the projection boundary is the backend-neutral model. It
35
+ is byte-identical in every configured project, whichever backend is chosen.
36
+ Nothing between the two markers may be defined in a backend's terms; naming one
37
+ to exclude it is correct and is not a leak.
38
+ -->
39
+
40
+ ## What gets tracked
41
+
42
+ Only **approved feature specs**, from `to-specs` onward. Never track debate
43
+ notes, kickstart output, or prototypes. The one exception is a prototype that
44
+ gates a decision, which may be tracked as a single item phrased as the decision
45
+ it resolves, never as a deliverable.
46
+
47
+ ## The work item
48
+
49
+ Every tracked thing is a **work item**. A work item has:
50
+
51
+ - a **key** — stable, assigned by Pathfinder, never by the tracker
52
+ - a **kind** — what sort of thing it is
53
+ - a **title** and a **body**
54
+ - zero or more **blocked-by** edges, naming other work items by key
55
+ - zero or more **tags**
56
+ - an ordered list of **chunks** (features only)
57
+ - a **parent** key (tickets only)
58
+
59
+ ### Keys
60
+
61
+ A key looks like `pathfinder:<kind>/<id>`. For a feature spec, the id is the
62
+ spec's number: `context/features/06-docs-site-scaffold.md` is
63
+ `pathfinder:feature/06`.
64
+
65
+ Keys are how a work item is recognised on a later run. Record the key inside the
66
+ published item so it can be found again without keeping a local index. Put it in
67
+ a marker block that the tracker will not render to a human:
68
+
69
+ ```text
70
+ <!-- pathfinder:work-item
71
+ key: pathfinder:feature/06
72
+ kind: feature
73
+ blocked-by: pathfinder:feature/03
74
+ tags: area:site, type:infra, agent:suitable
75
+ chunks-projection: checklist
76
+ -->
77
+ ```
78
+
79
+ Match on the key alone. Never match on the title — titles are edited by humans
80
+ and a title match will create duplicates or overwrite the wrong item.
81
+
82
+ **The title is written once, when the item is created, and is never reconciled
83
+ afterwards.** It is human-owned from that point on. This is a decision, not an
84
+ oversight, and it follows from the rule above: humans edit titles, so a
85
+ projection that rewrote them would overwrite that edit on every run. Renaming
86
+ the spec changes the pointer to it in the body, not the title.
87
+
88
+ **Prose alone is not sufficient for identity.** This marker is the one
89
+ machine-stable token in an otherwise prose contract. Everything else here can be
90
+ reworded; this cannot be removed on the grounds that the config is "just prose".
91
+ Without it there is no way to recognise an item on a second run, and publishing
92
+ stops being idempotent.
93
+
94
+ ### Kinds
95
+
96
+ Two kinds are defined.
97
+
98
+ - `feature` — one approved feature spec. One branch, one review, one merge.
99
+ - `ticket` — an independently assignable unit of execution inside a feature,
100
+ carrying its own blocked-by edges so that more than one agent can work a
101
+ feature at once. A ticket names its feature with `parent`.
102
+
103
+ **A ticket is not a delivery chunk.** Chunks are a planning device inside a spec;
104
+ tickets are units of execution. They may coincide, and they routinely will not: a
105
+ ticket may span several chunks, and a ticket may exist that belongs to no chunk
106
+ at all — a prefactor, or a decision that gates the rest. Nothing in this model
107
+ derives one from the other, and a projection that assumed `ticket == chunk` would
108
+ be wrong rather than simplified.
109
+
110
+ **Do not treat a chunk as permanently equal to a checkbox either.** Chunk
111
+ rendering is a *projection choice* named by `chunks-projection`, not a fact about
112
+ the model.
113
+
114
+ Feature and ticket are the **same work item**. They carry the same fields, the
115
+ same marker, the same edge semantics, the same tag model, and the same
116
+ idempotency rule. `chunks` is simply absent on a ticket, and `parent` is absent on
117
+ a feature — optional fields, not different shapes. **No rule anywhere may branch
118
+ on `kind`.**
119
+
120
+ ### Parent
121
+
122
+ A ticket names the feature it belongs to with `parent`, a key. Parentage is
123
+ **not** a blocking edge and must never be rendered as one: a ticket is not
124
+ blocked by its feature. Rendering parentage as a blocker produces a graph that
125
+ never unblocks.
126
+
127
+ ### Blocked-by edges
128
+
129
+ Edges name **keys**, never tracker issue numbers. Resolving a key to an issue
130
+ number is the projection's job, and the same edge must survive being pointed at a
131
+ different tracker.
132
+
133
+ An edge may name a work item that is **not in the set being published**. That is
134
+ normal — a feature can depend on one that was tracked earlier or not at all. Do
135
+ not drop such an edge and do not invent an item for it. Resolve it against the
136
+ tracker as a whole rather than against this run:
137
+
138
+ - if the key already has an item in the tracker, render that item's identifier,
139
+ exactly as an in-set edge is rendered;
140
+ - otherwise render the key and say plainly that it has no item here.
141
+
142
+ Being outside this run is not the same as being untracked, and an edge must not
143
+ claim it is.
144
+
145
+ Publish in dependency order, blockers first, so an edge can reference a real
146
+ identifier by the time it is written.
147
+
148
+ ### Tags
149
+
150
+ A tag is `namespace:value`. The namespace says what kind of statement the tag is
151
+ making; the value is free text.
152
+
153
+ Namespaces in use here:
154
+
155
+ - `area` — the part of the system touched, e.g. `area:site`, `area:cli`
156
+ - `type` — the nature of the work, e.g. `type:infra`, `type:content`
157
+ - `priority` — only when the project actually uses one, e.g. `priority:now`
158
+ - `agent` — suitability for autonomous work, e.g. `agent:suitable`,
159
+ `agent:needs-human`
160
+
161
+ **Tags are backend-neutral.** The list above is the whole model. How a tag
162
+ becomes a label, a field, or a line of text is a projection concern.
163
+
164
+ **Respect what the tracker already has.** Before introducing any tag value,
165
+ list the values the tracker already carries. If one already means the same
166
+ thing, use it and record the mapping below rather than creating a
167
+ near-duplicate. Never invent a taxonomy the team did not ask for, and never
168
+ apply a tag that is not in the mapping table.
169
+
170
+ ### From spec to work item
171
+
172
+ Every field above comes from the spec, and from nowhere else:
173
+
174
+ - **key** — `pathfinder:feature/<NN>`, from the spec's number.
175
+ - **kind** — `feature` for an approved feature spec.
176
+ - **title** — the spec's own title.
177
+ - **blocked-by** — the specs named under `## Dependencies`, each converted to
178
+ its key. A dependency that is not a feature spec — a decision, an external
179
+ system, `None` — produces no edge.
180
+ - **chunks** — the entries under `## Delivery Chunks`, in spec order.
181
+ - **tags** — **only** the values written in the spec's optional `## Tags`
182
+ section.
183
+
184
+ **Tags are never inferred.** Not from the title, not from the paths a spec
185
+ mentions, not from which part of the system it appears to touch. A spec with no
186
+ `## Tags` section has no tags, and an item published with none is correct and
187
+ expected — most projects never use them. Guessing a tag produces a taxonomy the
188
+ team did not choose, applied to work they did not classify, and it will look
189
+ authoritative on the board.
190
+
191
+ If a spec names a tag that this config's mapping table does not carry, **stop and
192
+ ask**. Do not apply it, and do not quietly drop it.
193
+
194
+ ### Body composition
195
+
196
+ The body is a **pure function of the spec**. The same spec must produce the same
197
+ body on every run. This is not tidiness: re-publishing decides what to do by
198
+ comparing the body it would write now against the one already there, so a body
199
+ that varies between runs rewrites every item forever while appearing to work.
200
+
201
+ Compose it in this order, and **omit any section whose source is absent** rather
202
+ than emitting it empty:
203
+
204
+ 1. a pointer to the spec this item projects
205
+ 2. the blocked-by edges
206
+ 3. the parent, on a ticket
207
+ 4. the delivery chunks
208
+ 5. the statement that the repository is canonical and that nothing here is read
209
+ back
210
+ 6. the marker block, last
211
+
212
+ **Never include anything derived from the run rather than the spec** — no date,
213
+ no timestamp, no run counter, no tally of what changed, no note of who published
214
+ it. Each of those differs on the next run and each would make every item look
215
+ modified. Never re-wrap or re-summarise text taken from the spec: copy it the
216
+ same way every time, or the same spec produces two different bodies.
217
+
218
+ How each of these elements *renders* is a projection concern. The order, the
219
+ omission rule, and the absence of run-derived content are not.
220
+
221
+ <!-- pathfinder:projection-boundary
222
+ This marker closes the neutral model opened by `pathfinder:model-start`.
223
+ Everything below is one projection. Keep exactly one of the two blocks that
224
+ follow, and delete this marker, the model-start marker, and the block you did
225
+ not keep.
226
+ -->
227
+
228
+ ## Projection to GitHub
229
+
230
+ *Keep this block for a GitHub Issues tracker; delete the local-files block below.*
231
+
232
+ - One work item → one issue.
233
+ - Title → the spec's title, prefixed with its number: `06 — Docs Site Scaffold`.
234
+ - The **spec pointer** → the first line of the body, naming the spec's path.
235
+ - The **repository-is-canonical statement** → a line near the end of the body,
236
+ before the marker, saying that nothing here is read back and that a ticked box
237
+ advances no state.
238
+ - The marker block goes at the **end** of the issue body.
239
+ - `blocked-by` → a `**Blocked by**` line naming each blocker as
240
+ `#<number> (<key>)`. A key with no issue in this repository renders as
241
+ `<key> — not tracked here`; a key that has one renders as `#<number> (<key>)`
242
+ whether or not it is part of the current run.
243
+ - `parent` → a `**Parent**` line naming the feature as `#<number> (<key>)`.
244
+ Never render parentage as a blocking edge.
245
+ - `chunks` under `chunks-projection: checklist` → a `## Delivery chunks` section
246
+ of `- [ ]` items in spec order. **A checked box means nothing to Pathfinder.**
247
+ The repository is canonical; a human ticking a box does not advance any state
248
+ and must never be read back.
249
+ - Tags → labels, via this mapping. Create a label only if nothing equivalent
250
+ exists. Replace the rows below with this project's own tags and the labels the
251
+ repository already carries.
252
+
253
+ | Tag | GitHub label | Colour |
254
+ |---|---|---|
255
+ | `area:site` | `area:site` | `1d76db` |
256
+ | `area:cli` | `area:cli` | `1d76db` |
257
+ | `type:infra` | `type:infra` | `5319e7` |
258
+ | `type:content` | `documentation` *(pre-existing — reused, not duplicated)* | `0075ca` |
259
+ | `agent:suitable` | `agent:suitable` | `0e8a16` |
260
+ | `agent:needs-human` | `agent:needs-human` | `d93f0b` |
261
+
262
+ ### Publishing, and re-publishing
263
+
264
+ **Ask the human before the first write to this tracker in a session.** Creating
265
+ issues on a shared repository is outward-facing and is not covered by ordinary
266
+ file-edit approval.
267
+
268
+ Publishing is **idempotent**. Re-running must be safe and must be provably inert
269
+ when nothing changed:
270
+
271
+ 1. Read every existing issue in the repository, open and closed, and index them
272
+ by the key in their marker block.
273
+ 2. For each work item, if no issue carries its key, create one.
274
+ 3. If an issue carries its key, compare the rendered body and the label set to
275
+ what would be published now. **If they are identical, do nothing at all** —
276
+ no edit, no comment, no label call. An unchanged item must produce zero
277
+ writes, not a write that happens to be a no-op.
278
+
279
+ **Compare normalized, never raw bytes.** A tracker is not obliged to hand a
280
+ body back exactly as it was sent, and APIs differ in whether they adjust
281
+ trailing whitespace. Where that happens, a naive byte comparison reports every
282
+ issue as changed on every run and rewrites all of them forever — which looks
283
+ like working sync and is not. Before comparing, strip trailing whitespace from
284
+ each line and collapse trailing blank lines at end of body, on **both** sides.
285
+ This costs nothing when the round-trip is exact, and is the difference between
286
+ inert and catastrophic when it is not. Compare label sets as sets, not as
287
+ ordered lists — the tracker does not preserve the order they were applied.
288
+ 4. If they differ, edit that issue in place. Never close and recreate.
289
+ 5. Never close an issue, never reopen one, and never touch an issue whose key is
290
+ absent from the current set — it belongs to work outside this run.
291
+
292
+ Report what was created, what was edited, and what was left alone.
293
+
294
+ ## Projection to local files
295
+
296
+ *Keep this block for a local Markdown tracker; delete the GitHub block above.*
297
+
298
+ - One work item → one file, `.work/<NN>-<slug>.md`. On first publish the files
299
+ are numbered from `01` in dependency order, so blockers sort first.
300
+
301
+ **`<NN>` is presentation and ordering only. It is never identity** — a file is
302
+ recognised by the key inside it, and nothing may match on the prefix.
303
+
304
+ A new file takes the **next unused prefix**, not the position it would occupy
305
+ in dependency order. Existing files are **never renumbered**: renaming a file
306
+ is deleting one and creating another, and re-publishing must never do that —
307
+ it would rewrite unrelated files every time a blocker was inserted. So after a
308
+ dependency change the prefixes no longer sort into dependency order, and that
309
+ is expected. Read the current order from the `**Blocked by**` lines, which are
310
+ reconciled. The prefixes are not.
311
+ - Title → an `# ` heading, prefixed with its number: `06 — Docs Site Scaffold`.
312
+ - The **spec pointer** → the first line of the body, naming the spec's path.
313
+ - The **repository-is-canonical statement** → a line near the end of the file,
314
+ before the marker, saying that nothing here is read back and that a ticked box
315
+ advances no state.
316
+ - The marker block goes at the **end** of the file.
317
+ - `blocked-by` → a `**Blocked by**` line naming each blocker as
318
+ `<file> (<key>)`. A key with no file under `.work/` renders as
319
+ `<key> — not tracked here`; a key that has one renders as `<file> (<key>)`
320
+ whether or not it is part of the current run.
321
+ - `parent` → a `**Parent**` line naming the feature as `<file> (<key>)`.
322
+ Never render parentage as a blocking edge.
323
+ - `chunks` under `chunks-projection: checklist` → a `## Delivery chunks` section
324
+ of `- [ ]` items in spec order. **A checked box means nothing to Pathfinder.**
325
+ The repository is canonical; a human ticking a box does not advance any state
326
+ and must never be read back.
327
+ - Tags → a `**Tags**` line listing them verbatim, comma-separated, in the order
328
+ given. There is no label object to create, so the mapping is the identity
329
+ mapping and no colour applies. Replace the rows below with this project's own
330
+ tags.
331
+
332
+ | Tag | Local rendering |
333
+ |---|---|
334
+ | `area:site` | `area:site` |
335
+ | `area:cli` | `area:cli` |
336
+ | `type:infra` | `type:infra` |
337
+ | `type:content` | `type:content` |
338
+ | `agent:suitable` | `agent:suitable` |
339
+ | `agent:needs-human` | `agent:needs-human` |
340
+
341
+ ### Publishing, and re-publishing
342
+
343
+ No approval is required to write under `.work/` — it is an ordinary file edit in
344
+ this repository and reaches nothing outside it.
345
+
346
+ Publishing is **idempotent**. Re-running must be safe and must be provably inert
347
+ when nothing changed:
348
+
349
+ 1. Read every existing file under `.work/` and index them by the key in their
350
+ marker block.
351
+ 2. For each work item, if no file carries its key, create one.
352
+ 3. If a file carries its key, compare its full contents to what would be written
353
+ now. **If they are identical, do nothing at all** — do not rewrite the file
354
+ with the same bytes. An unchanged item must leave its mtime untouched.
355
+ 4. If they differ, rewrite that file in place. Never delete and recreate.
356
+ 5. Never delete a file, and never touch a file whose key is absent from the
357
+ current set — it belongs to work outside this run.
358
+
359
+ Report what was created, what was edited, and what was left alone.