@natjswenson/devlog 0.13.0 → 0.14.1

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.
@@ -0,0 +1,245 @@
1
+ # Publishing one concept guide
2
+
3
+ `generationMode` is optional. Its absence and `"release"` retain the existing
4
+ release workflow, config shape and `publish-entry` behavior. Opt in with
5
+ `devlog set generationMode concept`; return with `devlog set generationMode release`.
6
+ The example config explicitly retains release mode. This setting selects the
7
+ agent workflow; it does not turn the legacy publisher into an evidence gate.
8
+
9
+ In concept mode, inspect the scan's genuine, eligible new final releases, then
10
+ choose at most one useful transferable outcome and one release anchor. Draft a
11
+ complete guide for the reader's project, with its exact copyable handoff first.
12
+ Do not manufacture a version/date to create a publishing slot. Verify that the
13
+ selected project/version/date matches the actual release before recording it.
14
+ If no candidate can meet the guide quality bar, publish zero guides and explain
15
+ what evidence or implementation is missing. The helper does not independently
16
+ verify Git tags and does not create a durable suppression ledger for other
17
+ candidates. A later scan can still return them.
18
+
19
+ Run structural lint, reproduce the guide, conduct an independent adaptation
20
+ trial in a disposable original project, and independently check the result.
21
+ Record observed commands/output and reviewer findings locally. For cover art,
22
+ open the full image and thumbnail and record semantic and visual observations
23
+ about the final PNG bytes. Fix blocking findings, regenerate the copy payload
24
+ and rerun affected checks after edits. Prepare these concrete artifacts before
25
+ publication; booleans asserting completion are insufficient.
26
+
27
+
28
+ ## Complete a normal concept Generate run
29
+
30
+ First read [concept-guides.md](concept-guides.md) for the complete draft, handoff,
31
+ execution and independent adaptation contract. On Codex also read
32
+ [codex-cover-art.md](codex-cover-art.md): generate the reviewed cover with the
33
+ available native image-generation tool and locally compose its typography. Do not
34
+ substitute the legacy SVG workflow on Codex just to finish. On Claude, follow the
35
+ existing local cover workflow and the true-color conversion note in that reference.
36
+ A missing required agent, execution or art capability leaves a local draft with a
37
+ specific blocker; it does not authorize bypassing the evidence gate. Do not adopt
38
+ legacy Generate's optional-cover fallback for a requested concept cover.
39
+
40
+ Use the CLI bundled relative to the loaded SKILL.md only after its read-only
41
+ `--version` invocation succeeds with available runtime dependencies. The commands
42
+ below show that form. If the script or dependencies are missing (including a Git
43
+ plugin install without node_modules), or the installation is standalone, use the
44
+ exact version fallback specified in SKILL.md in place of `node '<skill-root>/bin/devlog.js'`. Replace every placeholder
45
+ with recorded paths or validated config values and quote each shell argument.
46
+
47
+ ```bash
48
+ node '<skill-root>/bin/devlog.js' scan --json --summary
49
+ node '<skill-root>/bin/devlog.js' scan --json --project '<selected-project>'
50
+ node '<skill-root>/bin/devlog.js' lint-guide '<article-path>' --voice
51
+ node '<skill-root>/bin/devlog.js' prepare-guide --article '<article-path>' --brand '<brand-json>' --out '<new-preview-directory>'
52
+ ```
53
+
54
+ Honor an explicit project filter when scanning. Display the selected single
55
+ outcome and genuine project/version/date anchor, including any decision to create
56
+ zero guides. Follow Generate's voice, research, private-source and truthful
57
+ Shipped/Changelog rules; other releases are evidence candidates, not additional
58
+ articles to write. Prepare the guide, actually run its examples and the fresh
59
+ agent adaptation, independently check the result, and inspect the rendered preview.
60
+ Store the reports described below against the final article and exact copied
61
+ payload. An article edit invalidates hash-bound reports; rerun affected work and
62
+ explain the scope of evidence you retain.
63
+
64
+ Generate, persist, compose and inspect cover art using codex-cover-art.md. Then
65
+ prepare a new final preview (these helpers refuse existing output directories):
66
+
67
+ ```bash
68
+ node '<skill-root>/bin/devlog.js' prepare-guide --article '<article-path>' --brand '<brand-json>' --cover '<reviewed-cover.png>' --out '<new-final-preview-directory>'
69
+ ```
70
+
71
+ Open that preview and the full cover/thumbnail for the user. Confirm the copy
72
+ payload still matches the tested bytes. Hash final artifacts and write the execution,
73
+ adaptation and review reports plus `evidence.json`; these records stay in scratch,
74
+ outside the content repository. They must describe performed work, not planned work.
75
+
76
+ Clone the configured target branch into a new recorded scratch directory. The
77
+ clone root and content root are distinct when `targetDir` is set:
78
+
79
+ ```bash
80
+ mktemp -d
81
+ # Record that absolute directory as <run-root> before continuing.
82
+ git clone --depth=1 --branch '<branch>' 'https://github.com/<targetRepo>.git' '<run-root>/site'
83
+ ```
84
+
85
+ Record the clone, selected anchor and current artifact/report hashes in local
86
+ run-state. `<content-root>` means `<run-root>/site/<targetDir>` when configured,
87
+ otherwise `<run-root>/site`. Keep all Git operations at `<run-root>/site`.
88
+ Review the intended one-article content change, then stage it with the strict CLI:
89
+
90
+ ```bash
91
+ node '<skill-root>/bin/devlog.js' publish-guide --clone '<content-root>' --article '<article-path>' --evidence '<evidence-json>' --cover '<reviewed-cover.png>'
92
+ ```
93
+
94
+ The helper writes validated content only. Normal concept Generate must continue
95
+ through commit, push and deployment verification; do not end the run after this
96
+ command merely because it returned success. If it refuses an occupied or retired
97
+ identity, preserve that identity and report the collision. Do not use publish-entry
98
+ or manual copying to bypass it. Existing-post rewrite is a separate task.
99
+
100
+ Before committing, inspect the content diff and returned `firstEntryForProject`.
101
+ When true, follow SKILL.md Step 5b: register the project in the site's actual route
102
+ registry, build and prove the new route exists, then run relevant site tests.
103
+ For every concept guide, build/test the actual consumer as appropriate and inspect
104
+ the top handoff, code rendering, cover loading, feed thumbnail and link destinations.
105
+ The standalone preview alone does not prove site compatibility. Correct issues and
106
+ rerun relevant checks before push. If correcting article or cover bytes, regenerate
107
+ matching evidence and restart from a fresh clone rather than bypass immutability.
108
+
109
+ Stage only the article, cover, manifest and any required registry change, then
110
+ inspect the staged diff before committing. Use the configured Git identity and
111
+ existing target-repository conventions. Inspect the target repository's instructions
112
+ and branch/CI policy first. Where features must pass through `dev` and promotion,
113
+ create a feature branch from the required base and use that PR path; the configured
114
+ branch is the deployment destination, not permission to push past its policy.
115
+ The direct-push example below applies only when the target policy permits it:
116
+
117
+ ```bash
118
+ git -C '<run-root>/site' add -- '<article-relative-path>' '<cover-relative-path>' '<manifest-relative-path>'
119
+ # If needed, separately add the specific reviewed project-registry file.
120
+ git -C '<run-root>/site' diff --cached --stat
121
+ git -C '<run-root>/site' diff --cached
122
+ git -C '<run-root>/site' commit -m 'devlog: publish one concept guide'
123
+ git -C '<run-root>/site' push --no-tags origin '<branch>'
124
+ ```
125
+
126
+ Record the resulting commit and push outcome. If push fails, retain the clone,
127
+ report the error and stop; do not retry automatically, force-push or discard work.
128
+ After successful push, follow SKILL.md Step 6 for the configured site route. Poll
129
+ its actual article URL roughly every 45 seconds for at most five minutes:
130
+
131
+ ```bash
132
+ curl -s -o /dev/null -w '%{http_code}' -L '<actual-published-article-url>'
133
+ ```
134
+
135
+ An HTTP 200 alone can be a fallback page: open/read the response to verify this
136
+ article's title and top handoff, then verify the actual cover/thumbnail URLs load
137
+ and inspect the deployed layout. Respect the site's route convention instead of
138
+ assuming a new URL shape. If the route remains absent, inspect registry/build
139
+ results and report it as not live. If `siteUrl` is unset, report pushed but
140
+ unverified with the repository link, not published success. Retain local run-state
141
+ and evidence until the outcome is recorded; never delete scratch before checking
142
+ push/deployment. Finish with the live article link when verified, observed checks,
143
+ and material limitations. No extra approval step is introduced for publication
144
+ already authorized by a normal Generate request; explicit drafts remain drafts.
145
+
146
+ ## API and receipt
147
+
148
+ `publishGuide({ cloneDir, articlePath, evidencePath, coverPath? })` is exported
149
+ from `lib/publish_guide.mjs`. `cloneDir` is the existing content root inside the
150
+ publishing clone (including configured `targetDir`). `articlePath` and optional
151
+ `coverPath` are ordinary local paths relative to the invocation directory.
152
+ The API validates all evidence before writing content, then delegates to the
153
+ unchanged `publishEntry`. It never commits or pushes.
154
+
155
+ Evidence JSON schema 1:
156
+
157
+ ```json
158
+ {
159
+ "schema": 1,
160
+ "anchor": { "project": "example", "version": "v1.2.0", "date": "2026-09-09" },
161
+ "articleSha256": "<SHA-256 of exact article bytes>",
162
+ "agentPrompt": { "path": "agent-prompt.txt", "sha256": "<SHA-256>" },
163
+ "execution": { "path": "execution.json", "sha256": "<SHA-256>" },
164
+ "adaptation": { "path": "adaptation.json", "sha256": "<SHA-256>" },
165
+ "review": { "path": "review.json", "sha256": "<SHA-256>" },
166
+ "coverSha256": "<required only when supplying coverPath>"
167
+ }
168
+ ```
169
+
170
+ All SHA-256 values are lowercase 64-character hex strings. Referenced files
171
+ resolve relative to the evidence file, not the current working directory.
172
+ `agent-prompt.txt` must exactly equal the preview helper's copy payload: prompt,
173
+ two newlines, `<reference-guide>`, the trimmed article with the handoff block
174
+ removed, and closing `</reference-guide>`, with the helper's newline placement.
175
+ Use the `prepare-guide` output; do not maintain a second hand-edited payload.
176
+
177
+ Every execution/adaptation/review JSON report must contain these common fields:
178
+
179
+ ```json
180
+ {
181
+ "status": "passed",
182
+ "summary": "Concrete observations and scope of this run.",
183
+ "articleSha256": "<same exact article SHA-256>",
184
+ "agentPromptSha256": "<same exact payload SHA-256>"
185
+ }
186
+ ```
187
+
188
+ The execution report additionally contains a nonempty `commands` array:
189
+
190
+ ```json
191
+ {
192
+ "commands": [
193
+ {
194
+ "command": "node --test",
195
+ "exitCode": 0,
196
+ "output": { "path": "logs/tests.txt", "sha256": "<SHA-256>" }
197
+ }
198
+ ]
199
+ }
200
+ ```
201
+
202
+ Output files resolve relative to the execution report. They must contain actual
203
+ observations and match their hashes. Include each required successful check;
204
+ retain failed attempts separately and describe corrections in the summary.
205
+ Do not invent a successful command or replace actual output with a claim.
206
+
207
+ The adaptation report additionally identifies the starting and resulting
208
+ fixture (paths, immutable revisions, or durable artifact identifiers), and
209
+ records a nonempty list of independently observed checks:
210
+
211
+ ```json
212
+ {
213
+ "fixture": { "original": "fixtures/before", "result": "fixtures/after" },
214
+ "independentChecks": [
215
+ { "check": "Existing public imports", "observation": "No side effects under invalid CLI arguments", "passed": true }
216
+ ]
217
+ }
218
+ ```
219
+
220
+ The review report additionally contains `reviewer` (nonempty identifier) and
221
+ `blockingFindings: []`. Explain the substantive review in its `summary`; an
222
+ empty findings array alone is not a review. When a cover is supplied, both the
223
+ evidence and review must contain its `coverSha256`, and the review must also
224
+ contain nonempty `coverReview` observations. The decoded cover must be a
225
+ single-frame true-color 1600×900 PNG. Changes to art invalidate its visual review.
226
+
227
+ These are consistency checks on local records. Hashes do not establish that a
228
+ command actually ran, that a reviewer was independent, or that observations are
229
+ correct. The agent must perform and inspect the work before writing the records.
230
+ Do not describe `evidenceValidated: true` as independent execution verification.
231
+
232
+ ## Existing posts and failure behavior
233
+
234
+ This command refuses an occupied article, cover, manifest identity, or tombstone.
235
+ Even a manifest entry whose article is absent is occupied; use a deliberate
236
+ editorial repair workflow. Consolidating or rewriting published posts is a
237
+ separate explicitly scoped task that preserves URLs and existing identities.
238
+ There is no force option, migration, or change to old posts in this command.
239
+
240
+ Missing files, stale hashes, lint failures, failed/unobserved checks, anchor
241
+ mismatches and invalid covers fail before content mutation. After validation,
242
+ the existing publisher retains its existing article-then-manifest write behavior;
243
+ a disk failure is not an atomic rollback. Publish serially: neither helper
244
+ provides a multiprocess lock. Keep local evidence private unless deliberately
245
+ reviewed for inclusion in the public repository.
@@ -1,5 +1,5 @@
1
1
  {
2
- "comment": "Prose guardrails in SKILL.md that must survive edits (the `prose` array each pattern is a case-insensitive regex tested against the full SKILL.md text) and code-level guards elsewhere in this skill's source (the `code` array each pattern is tested against the file named by its own `file` field, not SKILL.md). Both are checked by tests/skill_contract.test.mjs. If you intentionally change one, update it here in the same commit and say why in the PR.",
2
+ "comment": "Prose guardrails in SKILL.md that must survive edits (the `prose` array \u2014 each pattern is a case-insensitive regex tested against the full SKILL.md text) and code-level guards elsewhere in this skill's source (the `code` array \u2014 each pattern is tested against the file named by its own `file` field, not SKILL.md). Both are checked by tests/skill_contract.test.mjs. If you intentionally change one, update it here in the same commit and say why in the PR.",
3
3
  "prose": [
4
4
  {
5
5
  "id": "immutable-entries",
@@ -19,7 +19,7 @@
19
19
  {
20
20
  "id": "gotchas-never-invented",
21
21
  "pattern": "never invented",
22
- "rationale": "Gotchas come from real history (fix commits, reverts, changelog) fabricating them breaks the product's honesty."
22
+ "rationale": "Gotchas come from real history (fix commits, reverts, changelog) \u2014 fabricating them breaks the product's honesty."
23
23
  },
24
24
  {
25
25
  "id": "reader-side-verify",
@@ -64,27 +64,27 @@
64
64
  {
65
65
  "id": "private-project-no-links",
66
66
  "pattern": "no post ever[\\s\\S]{0,10}links a commit for it",
67
- "rationale": "A private project's commits must never scan as public just because remoteMatches + branch-contains happen to hold losing this line reopens the dead-link-to-a-private-repo path."
67
+ "rationale": "A private project's commits must never scan as public just because remoteMatches + branch-contains happen to hold \u2014 losing this line reopens the dead-link-to-a-private-repo path."
68
68
  },
69
69
  {
70
70
  "id": "cover-composition-scope",
71
71
  "pattern": "never the raw draft file, never[\\s\\S]{0,10}## Changelog",
72
- "rationale": "Cover composition must draw only from title/tags/summary/## Shipped never the raw draft or Changelog losing this line reopens off-scope content leaking into an auto-composed image."
72
+ "rationale": "Cover composition must draw only from title/tags/summary/## Shipped \u2014 never the raw draft or Changelog \u2014 losing this line reopens off-scope content leaking into an auto-composed image."
73
73
  },
74
74
  {
75
- "id": "cover-never-blocks-publish",
76
- "pattern": "[Nn]ever block[\\s\\S]{0,10}publish on a missing style guide",
77
- "rationale": "A missing/uninstalled style guide must degrade gracefully (no cover this run), never abort the whole publish."
75
+ "id": "cover-required-before-publish",
76
+ "pattern": "publication is[\\s\\S]{0,5}blocked",
77
+ "rationale": "A missing/uninstalled style guide must stop the release so Generate mode cannot publish an entry without its required cover."
78
78
  },
79
79
  {
80
80
  "id": "cover-review-gate",
81
81
  "pattern": "this interactive review IS the quality gate for the cover",
82
- "rationale": "The rendered cover must be shown to the user before push, the same way Step 4 gates the prose losing this line reopens publishing an unreviewed cover."
82
+ "rationale": "The rendered cover must be shown to the user before push, the same way Step 4 gates the prose \u2014 losing this line reopens publishing an unreviewed cover."
83
83
  },
84
84
  {
85
85
  "id": "ground-truth-gate",
86
86
  "pattern": "verify each one with a git command run NOW",
87
- "rationale": "The 6-run audit found a post that published a provably false premise about the author's own repo (resume/v1.0.1: 'two of four packages never got tags' all four existed). Every repo-fact claim must be re-verified against git in-session before publish; losing this line reopens the fabricated-premise path."
87
+ "rationale": "The 6-run audit found a post that published a provably false premise about the author's own repo (resume/v1.0.1: 'two of four packages never got tags' \u2014 all four existed). Every repo-fact claim must be re-verified against git in-session before publish; losing this line reopens the fabricated-premise path."
88
88
  },
89
89
  {
90
90
  "id": "no-unrun-output",
@@ -104,7 +104,17 @@
104
104
  {
105
105
  "id": "cover-custom-illustration",
106
106
  "pattern": "cover that just re-renders the title in large text is a failure",
107
- "rationale": "First shipped version of this feature produced a shared text-heavy template with a rotating stock shape rejected as bland/repetitive. Losing this line reopens that regression. Independent of, not superseded by, the v0.9.0 geometry guard (cover-catalog-hero-overlap-guard, below): that guard is a mechanical check on an agent's rendered composition each time a cover is rendered; this line is a prose guardrail against a future SKILL.md/style-guide edit silently reintroducing the bland-template regression at the instruction level. Two different regression surfaces, both still worth guarding."
107
+ "rationale": "First shipped version of this feature produced a shared text-heavy template with a rotating stock shape \u2014 rejected as bland/repetitive. Losing this line reopens that regression. Independent of, not superseded by, the v0.9.0 geometry guard (cover-catalog-hero-overlap-guard, below): that guard is a mechanical check on an agent's rendered composition each time a cover is rendered; this line is a prose guardrail against a future SKILL.md/style-guide edit silently reintroducing the bland-template regression at the instruction level. Two different regression surfaces, both still worth guarding."
108
+ },
109
+ {
110
+ "id": "concept-draft-only",
111
+ "pattern": "draft-only; never fall through to Generate or publish-entry",
112
+ "rationale": "Explicit concept requests must not fall into automatic release publication."
113
+ },
114
+ {
115
+ "id": "native-art-no-implicit-fallback",
116
+ "pattern": "No implicit API fallback",
117
+ "rationale": "Optional Codex art must not create a new paid dependency or silently invoke a fallback."
108
118
  }
109
119
  ],
110
120
  "code": [
@@ -112,7 +122,7 @@
112
122
  "id": "cover-catalog-hero-overlap-guard",
113
123
  "file": "lib/render_cover.mjs",
114
124
  "pattern": "(?:getBoundingClientRect[\\s\\S]{0,400}hero-zone|hero-zone[\\s\\S]{0,400}getBoundingClientRect)",
115
- "rationale": "The catalog-icon/hero-zone overlap check must stay wired into renderCoverImage() losing it silently reopens the gap where a catalog icon (or two, connected by a line) can stand in for the required bespoke hero illustration."
125
+ "rationale": "The catalog-icon/hero-zone overlap check must stay wired into renderCoverImage() \u2014 losing it silently reopens the gap where a catalog icon (or two, connected by a line) can stand in for the required bespoke hero illustration."
116
126
  }
117
127
  ],
118
128
  "cli_commands_referenced": [
@@ -128,7 +138,10 @@
128
138
  "render-cover",
129
139
  "tombstone",
130
140
  "sync-entry",
131
- "assemble-post"
141
+ "assemble-post",
142
+ "lint-guide",
143
+ "prepare-guide",
144
+ "compose-art-cover"
132
145
  ],
133
146
  "_baseline_comment": "Baseline eval sets: deterministic, offline, $0 checks pinned against artifacts from real local runs. These gate `ci / devlog` alongside the unit tests. Every entry names the test that enforces it so tools/lint_baseline.py can verify the declaration is not aspirational.",
134
147
  "baseline": [
@@ -138,7 +151,15 @@
138
151
  "test": "tests/baseline.test.mjs",
139
152
  "corpus_glob": "evals/baseline/published/*.md",
140
153
  "min_corpus": 8,
141
- "rationale": "tests/lint_post.test.mjs unit-tests each rule against small crafted inputs and tests/evals.test.mjs drives the good/bad/irreproducible fixtures through the judge. All of those are hand-authored to exercise the rules; none answers whether the linter still accepts the real thing. evals/baseline/published/ holds eight entries copied verbatim from natejswenson.io posts that went through the full pipeline, were reviewed, and are live so 'this passes' means 'a human shipped this and stands behind it'. A rule that grows too strict starts rejecting work like this, and the cost lands at publish time on a real release. Deliberately a CURATED subset: of 61 published entries only 17 satisfy today's contract, the rest predating rules that landed later (the 5-10 tags-count range especially). Those are stale, not bad asserting over all 61 would encode 'the linter must accept its own history', a different and wrong requirement. Paired negative assertions (bad-post.md must still produce findings; the three required-section rules must still fire) stop the corpus passing vacuously against a linter that returned nothing."
154
+ "rationale": "tests/lint_post.test.mjs unit-tests each rule against small crafted inputs and tests/evals.test.mjs drives the good/bad/irreproducible fixtures through the judge. All of those are hand-authored to exercise the rules; none answers whether the linter still accepts the real thing. evals/baseline/published/ holds eight entries copied verbatim from natejswenson.io \u2014 posts that went through the full pipeline, were reviewed, and are live \u2014 so 'this passes' means 'a human shipped this and stands behind it'. A rule that grows too strict starts rejecting work like this, and the cost lands at publish time on a real release. Deliberately a CURATED subset: of 61 published entries only 17 satisfy today's contract, the rest predating rules that landed later (the 5-10 tags-count range especially). Those are stale, not bad \u2014 asserting over all 61 would encode 'the linter must accept its own history', a different and wrong requirement. Paired negative assertions (bad-post.md must still produce findings; the three required-section rules must still fire) stop the corpus passing vacuously against a linter that returned nothing."
155
+ },
156
+ {
157
+ "id": "real-concept-handoff",
158
+ "kind": "corpus",
159
+ "test": "tests/guide_draft.test.mjs",
160
+ "corpus_glob": "tests/fixtures/guide/*.md",
161
+ "min_corpus": 1,
162
+ "rationale": "Frozen ESM consolidation pilot that completed an independent ledger-project adaptation. Exact full-payload and clipboard tests plus malformed-handoff mutations retain its delivery contract; this does not claim future semantic review passed."
142
163
  }
143
164
  ]
144
165
  }