Turn accumulated lint and critique findings into permanent writer-profile entries, with a corpus step that filters out one-off false positives first.
pks writing learn <path> && pks writing apply <proposal>A review that ends at the report teaches nothing. learn, corpus, and apply are the bridge between "the linter found problems" and "the profile permanently knows about them", so the next post starts from a smarter baseline.
The step is deliberately two-phase: learn proposes, a human or agent reviews the proposal, and apply commits. Flag details are on the pks writing CLI reference.
learn reads _review/<stem>.WRITING-REPORT.json, which comes from linting and rubric scoring. Files without one are skipped, not errored.apply writes into it. See the writer profile.corpus step in step 3.pks writing learn blog-posts/my-post/da.md
This is non-interactive. It reads the file's last report, groups and dedupes the findings heuristically, and writes two sidecars under _review/: a machine-readable <stem>.LEARN.json where every proposed action carries an accept flag, and a human-readable <stem>.LEARN.md summary.
A folder recurses over *.md, skipping _review/, .pks/, and node_modules/:
pks writing learn blog-posts/
Restrict a folder run with a glob:
pks writing learn blog-posts/ --filter "da.md"
Files that produce zero actions have any stale LEARN.json and LEARN.md deleted rather than getting an empty proposal. Every run prints a machine-readable summary line:
RESULT: {"files":3,"skipped":1,"actions":11}
A term that appears in one post is often a false positive. corpus aggregates every per-post LEARN.json under a folder and keeps only the terms that recur:
pks writing corpus blog-posts/
The output is blog-posts/_corpus.LEARN.json plus a matching .md. Raise the threshold when you want stronger evidence:
pks writing corpus blog-posts/ --min-posts 3
--min-posts defaults to 2. --channel sets the channel label written into the proposal and defaults to blog.
Note.
corpusaggregates existingLEARN.jsonsidecars. It does not read report sidecars itself, so runpks writing learnacross the posts first.
Open the .LEARN.md for a readable summary, then edit the accept flags in the .LEARN.json. Actions left at accept: true are applied; actions set to false are counted as rejected and skipped. This is the only gate before a permanent profile change, so read it.
Preview first:
pks writing apply blog-posts/my-post/_review/da.LEARN.json --dry-run
Then commit:
pks writing apply blog-posts/my-post/_review/da.LEARN.json
Applying a corpus proposal works identically:
pks writing apply blog-posts/_corpus.LEARN.json
Each accepted action adds an allowlist term, an anglicism entry with its Danish alternative, or a dimension-tagged lesson. The store dedupes, so the operation is idempotent. Malformed actions missing a term, lesson, or dimension are recorded as warnings and skipped rather than aborting the run.
Do not commit.
applymutates the global profile store at~/.pks-cli/writing/, not just the current project. Every future lint and critique on this machine is affected.
pks writing profile show
The anglicism and allowlist counts should have grown by the number of accepted actions reported by apply. Re-lint the post to confirm the new terms take effect:
pks writing lint blog-posts/my-post/da.md
| Symptom | Cause and fix |
|---|---|
learn skips every file. | No _review/<stem>.WRITING-REPORT.json exists. Run pks writing lint or the prompt-and-accept pass first. |
No LEARN.json was written. | The file produced zero actions, so any stale proposal was deleted instead. |
corpus produces nothing. | No per-post LEARN.json sidecars exist under the folder, or no term reached --min-posts. |
apply reports many rejected actions. | Those actions carry accept: false in the JSON. Flip the ones you want. |
apply prints warnings and skips actions. | Those actions are missing a term, lesson, or dimension. The rest still applied. |
| A change on one machine did not follow you. | The profile is machine-local. Move it with pks writing profile export and import. |
learn come from.apply writes into, and move it between machines.