@profile-as-code/cli 0.3.6 → 0.4.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/README.md +536 -45
- package/dist/index.js +11097 -5110
- package/package.json +3 -2
- package/skills/README.md +40 -6
- package/skills/devto/SKILL.md +82 -0
- package/skills/devto/longform.yaml +33 -0
- package/skills/devto/post.md +96 -0
- package/skills/ghost/SKILL.md +85 -0
- package/skills/ghost/longform.yaml +36 -0
- package/skills/ghost/post.md +98 -0
- package/skills/github-org/SKILL.md +17 -2
- package/skills/github-org/checks.yaml +5 -2
- package/skills/github-org/playbook.md +2 -3
- package/skills/github-readme/SKILL.md +1 -1
- package/skills/github-readme/checks.yaml +5 -2
- package/skills/github-readme/playbook.md +1 -1
- package/skills/github-repo-meta/SKILL.md +82 -0
- package/skills/github-repo-meta/checks.yaml +50 -0
- package/skills/github-repo-meta/playbook.md +45 -0
- package/skills/hashnode/SKILL.md +79 -0
- package/skills/hashnode/longform.yaml +33 -0
- package/skills/hashnode/post.md +90 -0
- package/skills/hotmart/checks.yaml +5 -2
- package/skills/kofi/checks.yaml +6 -3
- package/skills/listmonk/SKILL.md +83 -0
- package/skills/listmonk/longform.yaml +33 -0
- package/skills/listmonk/post.md +93 -0
- package/skills/medium/SKILL.md +86 -0
- package/skills/medium/longform.yaml +33 -0
- package/skills/medium/post.md +96 -0
- package/skills/substack/SKILL.md +83 -0
- package/skills/substack/longform.yaml +36 -0
- package/skills/substack/post.md +91 -0
- package/skills/wordpress/SKILL.md +84 -0
- package/skills/wordpress/longform.yaml +33 -0
- package/skills/wordpress/post.md +99 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@profile-as-code/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "pasco
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "pasco \u2014 the profile-as-code CLI: init, validate, build, diff, edit (read / preview / write one contract field, comment-preserving), status + partial refine (task template), check (offline rules M6\u2013M8; catalog ships M3/M4), audit (offline findings projection), entity (the schema.org projection + paste-ready JSON-LD for surfaces the engine does not render), cite-check (deterministic citation matching over saved answer-engine answers, no network), export ai-policy (robots.txt + llms.txt + AI-POLICY.md: retrieval allowed, AI training blocked by default), export profile (the normalized, versioned profile object for programmatic consumers), preview (one self-contained HTML page of the rendered surfaces, written locally and never published), skills:export (copies the platform skills this package ships). Consumes @profile-as-code/core; never re-implements load/drift/geo/compile.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": [
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"node": ">=22"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
+
"marked": "^18.1.1",
|
|
60
61
|
"yaml": "^2.9.1",
|
|
61
62
|
"zod": "^4.6.7"
|
|
62
63
|
},
|
package/skills/README.md
CHANGED
|
@@ -12,7 +12,7 @@ skills/<plataforma>/
|
|
|
12
12
|
├── research/ # investigación versionada, con fuentes fechadas
|
|
13
13
|
│ └── SOTA-<año>.md
|
|
14
14
|
├── playbook.md # pasos de deploy manual + verificación post-pega
|
|
15
|
-
└── checks.yaml #
|
|
15
|
+
└── checks.yaml # premisas machine-readable: la unión con content/checks.yaml decide qué corre; los caps salen del catálogo del workspace cuando declara checks (entero, sin merge por fila) o de los defaults del paquete (SPEC §7)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## División de responsabilidades
|
|
@@ -53,10 +53,44 @@ skills/<plataforma>/
|
|
|
53
53
|
marca (≠ voz personal), handle-matrix checks (`handle-claimed`/`handle-consistent`,
|
|
54
54
|
offline en `pasco check`), trademark/impersonación. Ver `skills/github-org/`.
|
|
55
55
|
|
|
56
|
-
## Post skills (MVP, #264)
|
|
56
|
+
## Post skills (MVP, #264/#266/#361)
|
|
57
57
|
|
|
58
58
|
The week-1 post experiment uses a **different shape** from the profile skills:
|
|
59
|
-
`skills/<platform>/post.md` (judgment) +
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
`skills/<platform>/post.md` (judgment) + a machine-readable rule file.
|
|
60
|
+
`pasco post` reads them; `pasco refine` keeps reading `SKILL.md`.
|
|
61
|
+
|
|
62
|
+
- **Short** (`posting.yaml`): `linkedin`, `x` — char limits, hashtags.
|
|
63
|
+
- **Long-form** (`longform.yaml`, #266): `substack`, `ghost`, `medium`, `devto`,
|
|
64
|
+
`hashnode`, `wordpress`, `listmonk` — required fields (`title`/`dek`), an
|
|
65
|
+
editorial word band, and `export: api | paste` (Substack/Ghost are paste
|
|
66
|
+
surfaces). The word band is house style, not a platform limit: cite a source
|
|
67
|
+
before encoding a hard cap.
|
|
68
|
+
|
|
69
|
+
Every long-form channel also carries its judgment layer (#361): the `post.md` the
|
|
70
|
+
short channels already have, plus a `SKILL.md` that they do not ship.
|
|
71
|
+
|
|
72
|
+
- `post.md` — what a piece on that channel is for, its shape, its voice, and its
|
|
73
|
+
GEO and i18n policy, plus what `export: api | paste` means for the writing.
|
|
74
|
+
- `SKILL.md` — the channel skill document: mission, when the skill runs, the
|
|
75
|
+
flow, the pointers to the contract, and the suggested model in its frontmatter.
|
|
76
|
+
It is a strategy reference, not a bundle input: `pasco post task` carries the
|
|
77
|
+
contract and `post.md`, never this file.
|
|
78
|
+
|
|
79
|
+
**Do not use `pasco refine <channel>` on a post channel.** Refine is the
|
|
80
|
+
profile-refinement command; with a `SKILL.md` present it now resolves the file (it
|
|
81
|
+
used to answer "no skill yet") and emits a **profile** task that saves to
|
|
82
|
+
`profiles/<channel>/variants/<id>.md` — not a post workflow. The path for these
|
|
83
|
+
channels is `pasco post` with `longform.yaml`. Teaching the engine to tell a
|
|
84
|
+
profile skill from a post skill is a separate engine matter, not something a
|
|
85
|
+
skill can fix. (`checks.yaml` is not the cause: `pasco check` reads that catalog
|
|
86
|
+
and reports "no catalog" without failing when it is missing.)
|
|
87
|
+
|
|
88
|
+
`longform.yaml` stays the contract: required/optional fields, the word band and
|
|
89
|
+
`export` are declared there, and the prose explains them instead of restating
|
|
90
|
+
their numbers. A channel's *dated* platform sources live in the contract's
|
|
91
|
+
`sources[]`; a claim the contract does not already carry needs its own source
|
|
92
|
+
(URL + date + tier) before it enters either file. The post shape is Markdown
|
|
93
|
+
beside the rule file — not the `research/` + `playbook.md` + `checks.yaml` trio,
|
|
94
|
+
which belongs to the profile skills above.
|
|
95
|
+
|
|
96
|
+
Neither shape is projected to harnesses yet — see `docs/post-mvp.md`.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devto-post
|
|
3
|
+
description: >
|
|
4
|
+
Long-form post skill for DEV (Forem). Says what a DEV article is for and how
|
|
5
|
+
the agent writes one; the machine contract is skills/devto/longform.yaml
|
|
6
|
+
(required/optional fields, editorial word band, export: api). The judgment
|
|
7
|
+
layer — shape, voice, GEO, i18n — lives in post.md. The agent writes N
|
|
8
|
+
variants, the operator picks one, and the choice is recorded with
|
|
9
|
+
`pasco post choose`.
|
|
10
|
+
model: juicio (frontier) — choosing the technical claim and its trade-off is
|
|
11
|
+
judgment; the mechanical parts (markdown, front matter) are not
|
|
12
|
+
applies_to: devto
|
|
13
|
+
projection: agent-agnostic (any harness that reads Markdown and can run the CLI)
|
|
14
|
+
version: 0.1.0
|
|
15
|
+
status: draft (#361)
|
|
16
|
+
sources: skills/devto/longform.yaml
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# DEV (Forem) long-form — skill
|
|
20
|
+
|
|
21
|
+
## Mission
|
|
22
|
+
|
|
23
|
+
Turn one canonical seed into a DEV article that a developer can act on: one
|
|
24
|
+
claim, the smallest example that proves it, and the caveat that keeps it honest.
|
|
25
|
+
The skill is the judgment layer (D5); the deterministic part is the contract plus
|
|
26
|
+
`pasco post check`.
|
|
27
|
+
|
|
28
|
+
## When it runs
|
|
29
|
+
|
|
30
|
+
`pasco post task <seed> --format longform --channel devto` emits a task bundle
|
|
31
|
+
that points at the contract (`longform.yaml`) and the judgment file (`post.md`);
|
|
32
|
+
it does **not** carry this `SKILL.md`. This file is the channel's strategy
|
|
33
|
+
reference for whoever opens it directly: read the contract for the fields and the
|
|
34
|
+
band, then `post.md` for how to write.
|
|
35
|
+
|
|
36
|
+
Do **not** run `pasco refine devto` for this channel. Refine is the profile
|
|
37
|
+
command: with a `SKILL.md` now present it resolves this file instead of answering
|
|
38
|
+
"no skill for devto yet", and emits a **profile** task ("write N variants of the
|
|
39
|
+
devto profile") that saves to `profiles/devto/variants/<id>.md` — not a post
|
|
40
|
+
workflow. Telling a profile skill from a post skill is an engine decision, not
|
|
41
|
+
something this skill can fix.
|
|
42
|
+
|
|
43
|
+
## Flow
|
|
44
|
+
|
|
45
|
+
1. Read `longform.yaml` + `post.md`. Do not re-derive the limits: the contract
|
|
46
|
+
owns them.
|
|
47
|
+
2. Write N variants as YAML front matter + markdown body, at
|
|
48
|
+
`posts/<seed>/devto/devto-<n>.md`.
|
|
49
|
+
3. The operator picks one; record it with `pasco post choose`.
|
|
50
|
+
4. `pasco post check` — fix what it reports instead of explaining it away.
|
|
51
|
+
5. `pasco post export <seed> --target postiz` for the API draft payload.
|
|
52
|
+
6. Publish manually, then verify on the live page that the front matter landed.
|
|
53
|
+
|
|
54
|
+
## Contract pointers (single source each)
|
|
55
|
+
|
|
56
|
+
| Question | Home |
|
|
57
|
+
|---|---|
|
|
58
|
+
| required / optional fields | `longform.yaml` `fields` |
|
|
59
|
+
| editorial word band | `longform.yaml` `limits.words` (not a platform cap) |
|
|
60
|
+
| api vs paste | `longform.yaml` `export` |
|
|
61
|
+
| link policy | `longform.yaml` `links.policy` (`identity-ssot-only`) |
|
|
62
|
+
| dated platform sources | `longform.yaml` `sources[]` |
|
|
63
|
+
| how to write the piece | `post.md` |
|
|
64
|
+
|
|
65
|
+
## GEO and i18n
|
|
66
|
+
|
|
67
|
+
Public, indexed article page: the citation unit is the code block plus the
|
|
68
|
+
sentence that states what it proves. Write `en` by default; a Spanish piece is a
|
|
69
|
+
separate seed for that audience, never a duplicate of the English one. The
|
|
70
|
+
crawler stance is the repo's global `ai.policy` (`platforms/registry.yaml`, v1,
|
|
71
|
+
2026-10-27) — no per-channel rule is invented here.
|
|
72
|
+
|
|
73
|
+
## Verification
|
|
74
|
+
|
|
75
|
+
- `pasco post check` exit 0 (no `missing-field`, `word-count` or
|
|
76
|
+
`url-not-in-ssot` errors).
|
|
77
|
+
- Facts byte-identical to the SSOT; every URL from the identity SSOT.
|
|
78
|
+
- On the live page: title, tags and canonical URL arrived; the dek shows up as
|
|
79
|
+
the subtitle the channel renders, or not at all — never as a repeat of the
|
|
80
|
+
body's first line.
|
|
81
|
+
- Report any contract gap as a finding — the engine is not extended from a
|
|
82
|
+
skill (#361).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# devto long-form rules — machine-readable (#266)
|
|
2
|
+
#
|
|
3
|
+
# Read by `pasco post task|check|export` when the seed is `format: longform`.
|
|
4
|
+
# The word range is an EDITORIAL default (the operator's house style), NOT a
|
|
5
|
+
# platform limit: cite a platform source before encoding a hard cap here.
|
|
6
|
+
|
|
7
|
+
platform: devto
|
|
8
|
+
format: longform
|
|
9
|
+
export: api
|
|
10
|
+
settings:
|
|
11
|
+
__type: devto
|
|
12
|
+
fields:
|
|
13
|
+
required:
|
|
14
|
+
- title
|
|
15
|
+
- dek
|
|
16
|
+
optional:
|
|
17
|
+
- tags
|
|
18
|
+
- canonical
|
|
19
|
+
- publication
|
|
20
|
+
limits:
|
|
21
|
+
words:
|
|
22
|
+
min: 600
|
|
23
|
+
max: 3000
|
|
24
|
+
links:
|
|
25
|
+
policy: identity-ssot-only
|
|
26
|
+
structure:
|
|
27
|
+
- title
|
|
28
|
+
- dek
|
|
29
|
+
- sections
|
|
30
|
+
sources:
|
|
31
|
+
- url: https://developers.forem.com/api
|
|
32
|
+
date: 2026-10-29
|
|
33
|
+
note: "The Forem/DEV articles API takes title, tags and canonical_url; the body is markdown."
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# DEV (Forem) long-form post — skill (MVP)
|
|
2
|
+
|
|
3
|
+
> Long-form post skill. The contract is `longform.yaml`: required/optional
|
|
4
|
+
> fields, the editorial word band and `export` live there and are **not**
|
|
5
|
+
> restated here — this file is the judgment layer the agent reads before
|
|
6
|
+
> writing. The contract's `sources[]` carries the dated URL for every platform
|
|
7
|
+
> claim; re-verify before making a hard one, and cite a source (date + tier)
|
|
8
|
+
> before calling anything a limit.
|
|
9
|
+
|
|
10
|
+
## What a DEV piece is for
|
|
11
|
+
|
|
12
|
+
A technical piece for developers who will judge it by whether the code and the
|
|
13
|
+
claim hold up. The reader arrives from a tag feed, from search, or from a
|
|
14
|
+
cross-post on another channel — so the piece must stand alone: one technique,
|
|
15
|
+
one result, or one decision with its trade-off. It is not a career update and
|
|
16
|
+
not a newsletter issue.
|
|
17
|
+
|
|
18
|
+
## Shape
|
|
19
|
+
|
|
20
|
+
1. **Title** — the outcome the reader gets, in concrete verbs. No numbers you
|
|
21
|
+
cannot source, no curiosity gap the body does not pay off.
|
|
22
|
+
2. **Dek** — one line of scope: what the piece assumes and what it delivers.
|
|
23
|
+
The contract requires it as a field, and the engine's task bundle is explicit
|
|
24
|
+
that title and dek are fields, never the first lines of the body — so the dek
|
|
25
|
+
is delivered in the front matter, not repeated as the body's opening line. No
|
|
26
|
+
platform mapping is declared; confirm on the first publish whether DEV renders
|
|
27
|
+
it as a subtitle [?].
|
|
28
|
+
3. **Sections** — one H2 per step or idea. Show the smallest example that
|
|
29
|
+
proves the point, then the caveat. The contract fixes the three-part
|
|
30
|
+
structure; how many sections that takes is editorial.
|
|
31
|
+
|
|
32
|
+
## Rules
|
|
33
|
+
|
|
34
|
+
- **Front matter is the field contract.** Supply the fields `longform.yaml`
|
|
35
|
+
declares. A field the channel does not declare is not exported (`pasco post
|
|
36
|
+
check` warns `unknown-field`) — never smuggle it into the body instead.
|
|
37
|
+
- **Length:** the band in `limits.words` is the operator's editorial default,
|
|
38
|
+
not a DEV limit (the contract says so in its own header). Treat it as the
|
|
39
|
+
budget for a piece a developer finishes in one sitting, not as a ceiling to
|
|
40
|
+
fill.
|
|
41
|
+
- **Links:** `links.policy` is `identity-ssot-only` — every URL comes from the
|
|
42
|
+
identity SSOT. DEV syndicates, so `canonical` must point at the canonical
|
|
43
|
+
home from the SSOT, never at a second syndication copy.
|
|
44
|
+
- **Facts:** every number, date, version and URL is cited with
|
|
45
|
+
`{{fact:<pointer>}}`; the engine renders it and fails on a dangling pointer.
|
|
46
|
+
- **Voice:** first person, technical, no hype. Match `brandVoice`; the piece
|
|
47
|
+
should sound like an engineer explaining a decision, not a growth account.
|
|
48
|
+
|
|
49
|
+
## Export: `api`
|
|
50
|
+
|
|
51
|
+
The contract declares `export: api`: the body is markdown that the Forem/DEV
|
|
52
|
+
articles API receives, with the title, tags and canonical URL carried as payload
|
|
53
|
+
fields [OFICIAL — the contract's source, `sources[]` in `longform.yaml`, dated
|
|
54
|
+
2026-10-29]. Consequences for the writing:
|
|
55
|
+
|
|
56
|
+
- Markdown only. No raw HTML, no embeds that only an editor understands.
|
|
57
|
+
- `canonical` is a payload field, not a body line — do not paste the URL into
|
|
58
|
+
the text as a substitute.
|
|
59
|
+
- The export produces a Postiz draft payload; publishing stays manual (no
|
|
60
|
+
scheduler, no OAuth — `docs/post-mvp.md`).
|
|
61
|
+
|
|
62
|
+
## GEO (how this channel is read and cited)
|
|
63
|
+
|
|
64
|
+
DEV article pages are public and indexed, and the quotable unit is the snippet
|
|
65
|
+
plus the sentence that says what it proves. Keep the version and the date inside
|
|
66
|
+
the code block or the sentence around it; a table of unsourced benchmarks is not
|
|
67
|
+
citable. The crawler stance is the repo's global `ai.policy`
|
|
68
|
+
(`platforms/registry.yaml`, v1, 2026-10-27) — do not invent a per-channel rule.
|
|
69
|
+
Consistency check: every version, date and number matches the SSOT byte for
|
|
70
|
+
byte, exactly as on the profile surfaces.
|
|
71
|
+
|
|
72
|
+
## i18n
|
|
73
|
+
|
|
74
|
+
DEV's audience and tag vocabulary are English-first: write `en` by default. A
|
|
75
|
+
Spanish piece is its own seed for the hispano dev community, not a translation
|
|
76
|
+
of the English one. Do not publish the same text twice in two languages: two
|
|
77
|
+
canonical URLs of one text split the citation.
|
|
78
|
+
|
|
79
|
+
## Anti-patterns
|
|
80
|
+
|
|
81
|
+
- "X is dead" / "N tools you must know" listicles with no artifact behind them.
|
|
82
|
+
- An intro that restates the title for three paragraphs.
|
|
83
|
+
- A code block that cannot run, or a version pin that is never stated.
|
|
84
|
+
- Cross-posting without `canonical` — two copies then compete for one query.
|
|
85
|
+
- A benchmark number with no method, date or source.
|
|
86
|
+
|
|
87
|
+
## Output contract
|
|
88
|
+
|
|
89
|
+
Save each variant as YAML front matter + a markdown body at
|
|
90
|
+
`posts/<seed>/devto/devto-<n>.md`, then record the choice and check it:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
pasco post choose <seed> --channel devto --variant devto-1
|
|
94
|
+
pasco post check <seed> # missing-field · word-count · url-not-in-ssot · unknown-field
|
|
95
|
+
pasco post export <seed> --target postiz
|
|
96
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ghost-post
|
|
3
|
+
description: >
|
|
4
|
+
Long-form post skill for Ghost. Says what a Ghost essay/newsletter issue is for
|
|
5
|
+
and how the agent writes it; the machine contract is
|
|
6
|
+
skills/ghost/longform.yaml (required/optional fields, editorial word band,
|
|
7
|
+
export: paste). The judgment layer — shape, voice, GEO, i18n — lives in
|
|
8
|
+
post.md. The agent writes N variants, the operator picks one, and the choice is
|
|
9
|
+
recorded with `pasco post choose`; the deploy is a manual paste driven by the
|
|
10
|
+
generated checklist.
|
|
11
|
+
model: juicio (frontier) — the argument and the opening are judgment; the
|
|
12
|
+
mechanical parts (markdown, front matter, paste checklist) are not
|
|
13
|
+
applies_to: ghost
|
|
14
|
+
projection: agent-agnostic (any harness that reads Markdown and can run the CLI)
|
|
15
|
+
version: 0.1.0
|
|
16
|
+
status: draft (#361)
|
|
17
|
+
sources: skills/ghost/longform.yaml
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Ghost long-form — skill
|
|
21
|
+
|
|
22
|
+
## Mission
|
|
23
|
+
|
|
24
|
+
Turn one canonical seed into an essay worth a subscription: one argument, the
|
|
25
|
+
evidence that carries it, and an opening that earns the first scroll in an
|
|
26
|
+
inbox. Ghost is the surface the operator owns end to end (publication, domain,
|
|
27
|
+
archive), so the piece is written to be the citable copy — while every URL it
|
|
28
|
+
claims still comes from the identity SSOT.
|
|
29
|
+
|
|
30
|
+
## When it runs
|
|
31
|
+
|
|
32
|
+
`pasco post task <seed> --format longform --channel ghost` emits a task bundle
|
|
33
|
+
that points at the contract (`longform.yaml`) and the judgment file (`post.md`);
|
|
34
|
+
it does **not** carry this `SKILL.md`. This file is the channel's strategy
|
|
35
|
+
reference for whoever opens it directly: read the contract for the fields and the
|
|
36
|
+
band, then `post.md` for how to write.
|
|
37
|
+
|
|
38
|
+
Do **not** run `pasco refine ghost` for this channel. Refine is the profile
|
|
39
|
+
command: with a `SKILL.md` now present it resolves this file instead of answering
|
|
40
|
+
"no skill for ghost yet", and emits a **profile** task ("write N variants of the
|
|
41
|
+
ghost profile") that saves to `profiles/ghost/variants/<id>.md` — not a post
|
|
42
|
+
workflow. Telling a profile skill from a post skill is an engine decision, not
|
|
43
|
+
something this skill can fix.
|
|
44
|
+
|
|
45
|
+
## Flow
|
|
46
|
+
|
|
47
|
+
1. Read `longform.yaml` + `post.md`; the contract owns every limit.
|
|
48
|
+
2. Write N variants as YAML front matter + markdown body, at
|
|
49
|
+
`posts/<seed>/ghost/ghost-<n>.md`.
|
|
50
|
+
3. The operator picks one; record it with `pasco post choose`.
|
|
51
|
+
4. `pasco post check` — fix the findings.
|
|
52
|
+
5. `pasco post export <seed>` → `paste-checklist.md` (no body API in v1).
|
|
53
|
+
6. Paste the body and each field into the publication editor, tick the checklist,
|
|
54
|
+
and verify the live page before calling it published.
|
|
55
|
+
|
|
56
|
+
## Contract pointers (single source each)
|
|
57
|
+
|
|
58
|
+
| Question | Home |
|
|
59
|
+
|---|---|
|
|
60
|
+
| required / optional fields | `longform.yaml` `fields` |
|
|
61
|
+
| editorial word band | `longform.yaml` `limits.words` (not a platform cap) |
|
|
62
|
+
| api vs paste | `longform.yaml` `export: paste` (decision #266) |
|
|
63
|
+
| link policy | `longform.yaml` `links.policy` (`identity-ssot-only`) |
|
|
64
|
+
| dated platform sources | `longform.yaml` `sources[]` |
|
|
65
|
+
| how to write the piece | `post.md` |
|
|
66
|
+
|
|
67
|
+
## GEO and i18n
|
|
68
|
+
|
|
69
|
+
The publication's domain is the citable address; its archive is public, the email
|
|
70
|
+
copy is not. State the claim next to a heading so the section is extractable, and
|
|
71
|
+
keep every fact byte-identical to the profile. One publication, one language:
|
|
72
|
+
write `en` by default and treat a Spanish publication as a separate surface with
|
|
73
|
+
its own domain. The crawler stance is the repo's global `ai.policy`
|
|
74
|
+
(`platforms/registry.yaml`, v1, 2026-10-27).
|
|
75
|
+
|
|
76
|
+
## Verification
|
|
77
|
+
|
|
78
|
+
- `pasco post check` exit 0 (no `missing-field`, `word-count` or
|
|
79
|
+
`url-not-in-ssot` errors).
|
|
80
|
+
- The pasted body kept its heading levels, code fences and links; nothing was
|
|
81
|
+
flattened by the editor.
|
|
82
|
+
- Front matter fields arrived on the live page (title + dek where the theme
|
|
83
|
+
renders it); canonical, if set, matches the SSOT.
|
|
84
|
+
- Report any contract gap as a finding — the engine is not extended from a
|
|
85
|
+
skill (#361).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ghost long-form rules — machine-readable (#266)
|
|
2
|
+
#
|
|
3
|
+
# Read by `pasco post task|check|export` when the seed is `format: longform`.
|
|
4
|
+
# The word range is an EDITORIAL default (the operator's house style), NOT a
|
|
5
|
+
# platform limit: cite a platform source before encoding a hard cap here.
|
|
6
|
+
# `export: paste` in v1 even though Ghost has an Admin API: the issue defers the
|
|
7
|
+
# API channel and keeps Ghost a paste surface like Substack (#266 decision).
|
|
8
|
+
|
|
9
|
+
platform: ghost
|
|
10
|
+
format: longform
|
|
11
|
+
export: paste
|
|
12
|
+
settings:
|
|
13
|
+
__type: ghost
|
|
14
|
+
fields:
|
|
15
|
+
required:
|
|
16
|
+
- title
|
|
17
|
+
- dek
|
|
18
|
+
optional:
|
|
19
|
+
- preview
|
|
20
|
+
- tags
|
|
21
|
+
- canonical
|
|
22
|
+
- list
|
|
23
|
+
limits:
|
|
24
|
+
words:
|
|
25
|
+
min: 600
|
|
26
|
+
max: 3000
|
|
27
|
+
links:
|
|
28
|
+
policy: identity-ssot-only
|
|
29
|
+
structure:
|
|
30
|
+
- title
|
|
31
|
+
- dek
|
|
32
|
+
- sections
|
|
33
|
+
sources:
|
|
34
|
+
- url: https://ghost.org/docs/admin-api/
|
|
35
|
+
date: 2026-10-29
|
|
36
|
+
note: "Ghost ships an Admin API; v1 keeps Ghost a paste surface (paste checklist) per #266, not an API channel."
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Ghost long-form post — skill (MVP)
|
|
2
|
+
|
|
3
|
+
> Long-form post skill. The contract is `longform.yaml`: required/optional
|
|
4
|
+
> fields, the editorial word band and `export` live there and are **not**
|
|
5
|
+
> restated here — this file is the judgment layer the agent reads before
|
|
6
|
+
> writing. The contract's `sources[]` carries the dated URL for every platform
|
|
7
|
+
> claim; re-verify before making a hard one, and cite a source (date + tier)
|
|
8
|
+
> before calling anything a limit.
|
|
9
|
+
|
|
10
|
+
## What a Ghost piece is for
|
|
11
|
+
|
|
12
|
+
An essay or newsletter issue that lives on a publication the operator controls,
|
|
13
|
+
with its own domain and archive. That makes it the surface where the canonical
|
|
14
|
+
URL of a piece can exist: other channels point their `canonical` here *when the
|
|
15
|
+
SSOT already carries that URL* — never a URL the agent typed. The reader is
|
|
16
|
+
either a subscriber reading an email or someone who arrived from search; write
|
|
17
|
+
for both without writing twice.
|
|
18
|
+
|
|
19
|
+
## Shape
|
|
20
|
+
|
|
21
|
+
1. **Title** — the one claim the piece makes, stated so it still reads well in
|
|
22
|
+
an inbox list and in a search result.
|
|
23
|
+
2. **Dek** — the line that earns the next scroll: context, stake, or the
|
|
24
|
+
question the piece answers. The contract requires it; how the theme renders
|
|
25
|
+
it (subtitle, excerpt, nothing) varies per publication theme [?] — verify on
|
|
26
|
+
the first publish and record what you saw.
|
|
27
|
+
3. **Sections** — an essay argues, so give each section one move: the situation,
|
|
28
|
+
the tension, the evidence, the conclusion. H2 headings are the anchors a
|
|
29
|
+
reader (and an extractor) navigates by.
|
|
30
|
+
|
|
31
|
+
## Rules
|
|
32
|
+
|
|
33
|
+
- **Front matter is the field contract.** The fields `longform.yaml` declares are
|
|
34
|
+
the ones the channel consumes; anything else is dropped (`unknown-field`
|
|
35
|
+
warning) — do not re-add it in the body.
|
|
36
|
+
- **Length:** the band in `limits.words` is the operator's editorial default
|
|
37
|
+
(the contract says so). Aim for the piece the argument needs; the band is a
|
|
38
|
+
guardrail, not a quota.
|
|
39
|
+
- **Links:** `links.policy` is `identity-ssot-only`. A Ghost publication is a
|
|
40
|
+
URL surface like any other: every URL in the piece comes from the identity
|
|
41
|
+
SSOT, and `canonical` is only set when the SSOT already carries the target.
|
|
42
|
+
- **Facts:** `{{fact:<pointer>}}` for every number, date and URL; a dangling
|
|
43
|
+
pointer fails the check.
|
|
44
|
+
- **Voice:** first person, essayistic, no hype. Match `brandVoice`; a newsletter
|
|
45
|
+
issue is the one surface where a longer voice can breathe — do not turn that
|
|
46
|
+
into padding.
|
|
47
|
+
|
|
48
|
+
## Export: `paste`
|
|
49
|
+
|
|
50
|
+
The contract declares `export: paste` even though Ghost ships an Admin API: v1
|
|
51
|
+
keeps Ghost a paste surface like Substack, by decision (#266), and the contract's
|
|
52
|
+
`sources[]` records the Admin API docs [OFICIAL, dated 2026-10-29]. Consequences
|
|
53
|
+
for the writing and the deploy:
|
|
54
|
+
|
|
55
|
+
- The body is **pasted into the publication editor**; formatting must survive a
|
|
56
|
+
paste. Keep markdown simple: headings, paragraphs, lists, code fences, block
|
|
57
|
+
quotes. No raw HTML, no footnotes the editor may flatten, no tables unless you
|
|
58
|
+
re-check them after pasting.
|
|
59
|
+
- `pasco post export` writes a paste checklist, not a publish: the deploy is
|
|
60
|
+
manual and the checklist is the record of what was pasted.
|
|
61
|
+
- Fields are pasted one by one — treat the checklist as the field contract's
|
|
62
|
+
human-readable form and tick each line.
|
|
63
|
+
|
|
64
|
+
## GEO (how this channel is read and cited)
|
|
65
|
+
|
|
66
|
+
The publication's own domain is the citable address: its archive pages are
|
|
67
|
+
public and indexable, while the email copy is not. That is why the canonical URL
|
|
68
|
+
matters here more than on a network — the citation unit is a section heading
|
|
69
|
+
plus the claim under it, and it resolves to a URL the operator owns. The crawler
|
|
70
|
+
stance is the repo's global `ai.policy` (`platforms/registry.yaml`, v1,
|
|
71
|
+
2026-10-27); no per-channel rule is invented here. Consistency check: the piece's
|
|
72
|
+
facts are byte-identical to the profile and to every other channel's projection.
|
|
73
|
+
|
|
74
|
+
## i18n
|
|
75
|
+
|
|
76
|
+
A Ghost publication is per language: one publication, one audience language.
|
|
77
|
+
Write `en` by default; a Spanish publication is a separate publication with its
|
|
78
|
+
own domain and archive, and the piece belongs to exactly one of them. Do not
|
|
79
|
+
publish a machine translation of the same text under the same canonical.
|
|
80
|
+
|
|
81
|
+
## Anti-patterns
|
|
82
|
+
|
|
83
|
+
- An essay whose first two paragraphs are a table of contents.
|
|
84
|
+
- A `canonical` invented for the occasion — it must come from the SSOT.
|
|
85
|
+
- Rich HTML that the editor flattens on paste, discovered after publishing.
|
|
86
|
+
- Email-only framing in a piece that is also the public archive page.
|
|
87
|
+
- A newsletter issue that is a link dump with no argument.
|
|
88
|
+
|
|
89
|
+
## Output contract
|
|
90
|
+
|
|
91
|
+
Save each variant as YAML front matter + a markdown body at
|
|
92
|
+
`posts/<seed>/ghost/ghost-<n>.md`, then record the choice and check it:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
pasco post choose <seed> --channel ghost --variant ghost-1
|
|
96
|
+
pasco post check <seed> # missing-field · word-count · url-not-in-ssot · unknown-field
|
|
97
|
+
pasco post export <seed> # paste: writes posts/<seed>/ghost/paste-checklist.md
|
|
98
|
+
```
|
|
@@ -77,11 +77,26 @@ se cita, se ignora.
|
|
|
77
77
|
2. `pasco check` — handle-claimed / handle-consistent OK (offline) + `url-live` de
|
|
78
78
|
cada handle claimed (network, verificación manual).
|
|
79
79
|
3. `pasco build github-org --lang en` → `profiles/github-org/README.md`.
|
|
80
|
-
4. Copiar el README al repo del org (deploySpec: git; el humano mergea
|
|
81
|
-
repo-governance).
|
|
80
|
+
4. Copiar el README al repo del org (deploySpec: git; el humano mergea).
|
|
82
81
|
5. Verificación post-deploy: cada badge/link del README resuelve; los handles
|
|
83
82
|
pending NO figuran como surfaces vivas.
|
|
84
83
|
|
|
84
|
+
## Límite de badges (declarado, #428)
|
|
85
|
+
|
|
86
|
+
El renderer dibuja un badge sólo para los slugs curados del mapa `BADGE` (hoy
|
|
87
|
+
`github-org`, `x`, `bluesky`, `substack`, `instagram`, `tiktok`, `youtube`,
|
|
88
|
+
`spotify`, `reddit`, `mastodon`, `soundcloud`). Un handle `claimed` sobre un slug
|
|
89
|
+
del enum sin entrada — `linkedin`, `upwork`, `github-readme`, `kofi`, … — **no
|
|
90
|
+
renderiza badge y no emite warning**: el silencio es el contrato, no un bug. El
|
|
91
|
+
handle no se pierde: suma al footer, aparece en `## Links` y entra al `sameAs`
|
|
92
|
+
del JSON-LD. En el README no prometas un badge para un slug fuera del set; si el
|
|
93
|
+
badge es necesario, la salida es curar la entrada (agregarla al mapa) o abrir la
|
|
94
|
+
decisión del fallback genérico (opción A de #428, hoy deliberadamente no
|
|
95
|
+
implementada). El tamaño del set y el del hueco son **una lectura del test**, no
|
|
96
|
+
una cifra escrita acá: `packages/renderers/github-org/tests/render.test.ts` pinea
|
|
97
|
+
ambos conteos, así que curar una entrada o agregar un fallback con la forma actual
|
|
98
|
+
de badge rompe el test primero.
|
|
99
|
+
|
|
85
100
|
## GEO
|
|
86
101
|
|
|
87
102
|
`schema.org/Organization` JSON-LD con `@id` = el link con `role: identity` (ausente si
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# checks.yaml — criterios machine-readable para `pasco check github-org`
|
|
2
|
-
#
|
|
2
|
+
# Misma rule catalog que el schema; la unión con content/checks.yaml decide qué
|
|
3
|
+
# premisas corren (fact-present) y los caps de caracteres salen del catálogo del
|
|
4
|
+
# workspace cuando declara checks — entero, sin merge por fila — o de los defaults
|
|
5
|
+
# del paquete; nunca de estas filas (SPEC §7, pasco check).
|
|
3
6
|
# v1.1/§17: handle-claimed y handle-consistent se EJECUTAN offline en pasco check
|
|
4
7
|
# (drift codes); url-live es la verificación de red (skill, no drift).
|
|
5
8
|
|
|
@@ -19,7 +22,7 @@
|
|
|
19
22
|
platform: github-org
|
|
20
23
|
rule: fact-present
|
|
21
24
|
value: true
|
|
22
|
-
note: "
|
|
25
|
+
note: "El README que escribió `pasco build` se compara contra el render actual del SSOT en los dos sentidos — URL, fecha o número agregado u omitido, y texto sin token que lo explique (WARN, D12; `pasco audit` lo gradúa). `link-in-identity` es una premisa aparte."
|
|
23
26
|
|
|
24
27
|
- id: org-links-identity
|
|
25
28
|
platform: github-org
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
- Workspace brand (`pasco init --flavor brand`) con `content/` lleno y
|
|
6
6
|
`pasco validate` en verde.
|
|
7
|
-
- Org de GitHub creado (privado hasta pasar el checklist de public-readiness
|
|
8
|
-
repo-governance).
|
|
7
|
+
- Org de GitHub creado (privado hasta pasar el checklist de public-readiness).
|
|
9
8
|
|
|
10
9
|
## Pasos
|
|
11
10
|
|
|
@@ -17,7 +16,7 @@
|
|
|
17
16
|
3. **Build:** `pasco build github-org --lang en` →
|
|
18
17
|
`profiles/github-org/README.md`.
|
|
19
18
|
4. **Deploy:** copiá el README al repo `org/org` del GitHub org y commiteá en tu
|
|
20
|
-
branch + PR — el humano mergea
|
|
19
|
+
branch + PR — el humano mergea.
|
|
21
20
|
5. **Verificación post-deploy:** cada badge y link del README resuelve; los
|
|
22
21
|
handles `pending` NO figuran como surfaces vivas; el JSON-LD `Organization`
|
|
23
22
|
está embebido (GEO §17.1).
|
|
@@ -93,7 +93,7 @@ Liderá con #3 filtrado por #1: **stewardship** (proyectos vivos con proof), no
|
|
|
93
93
|
1. **Stewardship** (por defecto): lidera con open source + evidencia.
|
|
94
94
|
2. **Soberanía**: lidera con la tesis (IA soberana, economía agéntica), proyectos como prueba.
|
|
95
95
|
3. **Investigador**: lidera con publicaciones/DOI, proyectos como aplicación.
|
|
96
|
-
- Cada variante: `variantId` = `
|
|
96
|
+
- Cada variante: `variantId` = `githubreadme-<lang>-<n>` (ej. `githubreadme-en-1`).
|
|
97
97
|
- El humano elige; se registra con `pasco refine github-readme --choose <id>` →
|
|
98
98
|
escribe `content/active.yaml`.
|
|
99
99
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# checks.yaml — criterios machine-readable para `pasco check github-readme`
|
|
2
|
-
#
|
|
2
|
+
# Misma rule catalog que el schema; la unión con content/checks.yaml decide qué
|
|
3
|
+
# premisas corren (fact-present) y los caps de caracteres salen del catálogo del
|
|
4
|
+
# workspace cuando declara checks — entero, sin merge por fila — o de los defaults
|
|
5
|
+
# del paquete; nunca de estas filas (SPEC §7, pasco check).
|
|
3
6
|
|
|
4
7
|
- id: gh-bio-max
|
|
5
8
|
platform: github-readme
|
|
@@ -11,7 +14,7 @@
|
|
|
11
14
|
platform: github-readme
|
|
12
15
|
rule: fact-present
|
|
13
16
|
value: true
|
|
14
|
-
note: "
|
|
17
|
+
note: "El README que escribió `pasco build` se compara contra el render actual del SSOT en los dos sentidos: URL, fecha o número que la superficie agrega u omite, y texto que ningún token explica (WARN, D12; `pasco audit` lo gradúa). `link-in-identity` es una premisa aparte."
|
|
15
18
|
|
|
16
19
|
- id: gh-links-identity
|
|
17
20
|
platform: github-readme
|
|
@@ -8,7 +8,7 @@ Sin pegar a mano, sin API.
|
|
|
8
8
|
|
|
9
9
|
1. **Refine**: `pasco refine github-readme --variants 3` → el agente (esta skill)
|
|
10
10
|
produce 3 variantes en prosa. Elegí una:
|
|
11
|
-
`pasco refine github-readme --choose
|
|
11
|
+
`pasco refine github-readme --choose githubreadme-en-2`.
|
|
12
12
|
2. **Build**: `pasco build github-readme` → si hay una variante activa con cuerpo,
|
|
13
13
|
escribe ese README completo VERBATIM en `profiles/github-readme/README.md`;
|
|
14
14
|
sin variante, regenera el snapshot determinista del SSOT (control de verdad).
|