@profile-as-code/cli 0.3.7 → 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 +142 -47
- package/dist/index.js +2082 -535
- package/package.json +1 -1
- package/skills/README.md +1 -1
- package/skills/github-org/SKILL.md +16 -0
- package/skills/github-org/checks.yaml +4 -1
- package/skills/github-readme/SKILL.md +1 -1
- package/skills/github-readme/checks.yaml +4 -1
- package/skills/github-readme/playbook.md +1 -1
- package/skills/hotmart/checks.yaml +4 -1
- package/skills/kofi/checks.yaml +5 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The `pasco` CLI — `@profile-as-code/cli`. Consumes `@profile-as-code/core`
|
|
4
4
|
(`load`/`driftCheck`/`geoCheck`/`compile`) — it never re-implements them. The
|
|
5
|
-
contract source is
|
|
5
|
+
contract source is [the profile schema](https://github.com/ChristianLuciani/profile-as-code/blob/main/schema/profile.schema.json) (D3); Zod is generated in core.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -22,14 +22,14 @@ server, no account.
|
|
|
22
22
|
> npm-publish attestations + SLSA provenance v1; `0.3.6` is prepared in this release
|
|
23
23
|
> (CHANGELOG `[0.3.6]`). The `0.3.0` tarball carries none because it was published while
|
|
24
24
|
> the source repository was private.
|
|
25
|
-
> Source install: [User Guide §2](
|
|
25
|
+
> Source install: [User Guide §2](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/USER-GUIDE.md#2-install-the-pasco-cli).
|
|
26
26
|
|
|
27
27
|
## Get a workspace
|
|
28
28
|
|
|
29
29
|
`pasco` reads a **workspace folder** (`content/*.yaml` + `.pascorc`) by configured
|
|
30
30
|
path. That folder does not have to be a git repository, and it does not have to live
|
|
31
31
|
on GitHub — a repo is the **recommended** place to keep it, not a requirement
|
|
32
|
-
([SPEC §9](
|
|
32
|
+
([SPEC §9](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/SPEC.md)). Three on-ramps, each with its capability difference
|
|
33
33
|
stated up front:
|
|
34
34
|
|
|
35
35
|
| On-ramp | What you get | Automation + history | Account + repo |
|
|
@@ -48,9 +48,44 @@ The two no-account on-ramps keep the whole engine command set except `pasco diff
|
|
|
48
48
|
history — `pasco init` is minimal on purpose, and the complete package lives in
|
|
49
49
|
the template. **Where each on-ramp is discovered:** the template repo's own README,
|
|
50
50
|
this package page, and — in the repository — the
|
|
51
|
-
[User Guide §3](
|
|
52
|
-
(EN).
|
|
53
|
-
|
|
51
|
+
[User Guide §3](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/USER-GUIDE.md#3-create-your-workspace-profile-data-username)
|
|
52
|
+
(EN). The links point at the public repository; the two commands above are the
|
|
53
|
+
standalone path.
|
|
54
|
+
|
|
55
|
+
## How the cycle resolves (three questions)
|
|
56
|
+
|
|
57
|
+
**What does `fact-present` compare?**
|
|
58
|
+
|
|
59
|
+
> **Symmetric**: a URL the surface adds → `dangling-ref`; a URL it omits, a date or
|
|
60
|
+
> number either side carries and the other does not, or a projected text difference
|
|
61
|
+
> with no token to attribute → `contradiction`.
|
|
62
|
+
|
|
63
|
+
> All are WARN (D12: facts warn), and a targeted surface with no artifact on disk
|
|
64
|
+
> reports the rule as **not run**, never as OK
|
|
65
|
+
|
|
66
|
+
Canonical home: [SPEC §7](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/SPEC.md#7-core-interfaces-deep-module).
|
|
67
|
+
|
|
68
|
+
**In what order does `build` choose an artifact?**
|
|
69
|
+
|
|
70
|
+
> **The artifact source is resolved in ONE fixed order** (`renderArtifact`): an
|
|
71
|
+
> active variant body (#238, written verbatim as the platform artifact) → the
|
|
72
|
+
> platform renderer from the generated renderer map → the shared paste source
|
|
73
|
+
> (#239, a manual-paste surface writes `profiles/<platform>.md`). First match wins;
|
|
74
|
+
> `preview` and `edit --check` read the same tree.
|
|
75
|
+
|
|
76
|
+
Canonical home: [SPEC §7](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/SPEC.md#7-core-interfaces-deep-module).
|
|
77
|
+
|
|
78
|
+
**Where do the per-platform character caps come from?**
|
|
79
|
+
|
|
80
|
+
> The **limit executor** measures the workspace's `content/checks.yaml` when it
|
|
81
|
+
> declares checks, else the caps shipped with the package (#240); the `max-chars`
|
|
82
|
+
> rows the shipped `skills/<platform>/checks.yaml` declare are **declared and not
|
|
83
|
+
> executed** — no skill row reaches that executor.
|
|
84
|
+
|
|
85
|
+
> a platform with no sourced cap reads `cap unknown` in text mode (`limit: null` in
|
|
86
|
+
> `--json`)
|
|
87
|
+
|
|
88
|
+
Canonical home: [SPEC §7](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/SPEC.md#7-core-interfaces-deep-module). The gap these clauses state is tracked in #455.
|
|
54
89
|
|
|
55
90
|
## Commands
|
|
56
91
|
|
|
@@ -58,22 +93,23 @@ commands above are the standalone path.
|
|
|
58
93
|
|---|---|---|
|
|
59
94
|
| `pasco init [dir]` | Scaffold a profile-data workspace (`content/identity.yaml`, `README.md`, `.pascorc`) — `pasco validate`-clean | ✅ |
|
|
60
95
|
| `pasco validate [--json]` | Schema **fail** (exit 1) + drift/geo **warn** (exit 0) — D12 | ✅ |
|
|
61
|
-
| `pasco doctor [--json]` | **Engine · schema · workspace** on one line (#348): the installed CLI version, the profile-contract version this build validates (from the schema `$id`), the resolved workspace and whether it loads, plus the **effective** `.pascorc` engine requirement (the strictest declaration in every config the resolution consulted, with its file). `--json` emits `{ engine, schema, workspace, requirement: { declared, met, problem?, source? } }
|
|
62
|
-
| `pasco build [platform] [--lang]` | Deterministic base texts per platform+lang; platforms with a renderer (the eight registered in the generated renderer map) write `profiles/<platform>/README.md` + bundled media; renderer-less manual surfaces with a bio row write `profiles/<platform>.md` (#239). Empty render = no-trace unbuild of the generated output, preserving `profiles/<platform>/variants/` (chosen-variant input, #238; pure profiles/, §15.1 — v1.7). Media in `profiles/<platform>/assets/` + `assets/manifest.json` (M8/§18.6 — see
|
|
96
|
+
| `pasco doctor [--json]` | **Engine · schema · workspace** on one line (#348): the installed CLI version, the profile-contract version this build validates (from the schema `$id`), the resolved workspace and whether it loads, plus the **effective** `.pascorc` engine requirement (the strictest declaration in every config the resolution consulted, with its file). `--json` emits `{ engine, schema, workspace, requirement: { declared, met, state, problem?, source? } }`, where `state` is `absent` · `met` · `NOT MET` · `INVALID` — an absent declaration is a state, never a silent pass. Exit 0 healthy · 1 unhealthy · 2 usage; offline, no clock, no writes | ✅ v1 |
|
|
97
|
+
| `pasco build [platform] [--lang]` | Deterministic base texts per platform+lang; platforms with a renderer (the eight registered in the generated renderer map) write `profiles/<platform>/README.md` + bundled media; renderer-less manual surfaces with a bio row write `profiles/<platform>.md` (#239). Empty render = no-trace unbuild of the generated output, preserving `profiles/<platform>/variants/` (chosen-variant input, #238; pure profiles/, §15.1 — v1.7). Media in `profiles/<platform>/assets/` + `assets/manifest.json` (M8/§18.6 — see [the media pipeline guide](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/media-pipeline.md)) | ✅ |
|
|
63
98
|
| `pasco preview [--out <dir>] [--no-open] [--lang <l>] [--now <iso>] [--json]` | The **local view** (#282): **one** self-contained HTML file of the surfaces `pasco build` writes — inline CSS, no `<script>`, no external asset, no HTTP server. Sections are the published surfaces (`#github-readme`, `#linkedin`, …) plus the ones this language does not publish, named with their reason; the check state is the same report `pasco check` prints, and the text is `build`'s own render decision tree. Default `<workspace>/.pasco/preview/index.html`, then opened in the browser; `--out <dir>` writes and opens **nothing** (the CI-artifact transport). A privacy gate refuses the write (exit 1) on an absolute machine path, a credential shape, an external asset, active content, or a source outside the workspace | ✅ v1 |
|
|
64
|
-
| `pasco edit <contract-path> [--set <text> [--check]]` | Resolve **one** contract field by contract path; `--set` **writes it** (comment-preserving range splice, validated, atomic) and `--set --check` previews the **published lines the edit changes** (or says it changes nothing). Epic #247, S1–S5 landed | ✅ S1–S5 (read · preview · write · surface impact · cap pre-flight · machine contract) |
|
|
99
|
+
| `pasco edit <contract-path> [--set <text> [--check]]` · `--list [<block>]` · `--create <root> [--check]` | Resolve **one** contract field by contract path; `--set` **writes it** (comment-preserving range splice, validated, atomic) and `--set --check` previews the **published lines the edit changes** (or says it changes nothing). Covers the answer-engine surface too: `identity.definition.<lang>`, `faq[id=<slug>,lang=<l>].q|.a` and `faq[id=<slug>].source` (#435). `--list` is the read-only discovery surface: every addressable row, one per line, `--json` as `{ addresses: [{ address, kind, readOnly }], warnings }` (#411). `--create <root>` seeds a root the workspace does not declare yet (`content/faq.yaml`): schema-valid template, refused when the root exists, same validation + cap pre-flight, `--check` previews it (#435). Epic #247, S1–S5 landed | ✅ S1–S5 (read · preview · write · surface impact · cap pre-flight · machine contract) + `--list` + `--create` |
|
|
65
100
|
| `pasco diff [--json]` | Changes since the last `release-*` tag (git-based → CHANGELOG hint) | ✅ |
|
|
66
|
-
| `pasco status [platform] [--mark <ledgerKey> <
|
|
101
|
+
| `pasco status [platform] [--mark <ledgerKey> <platform>] [--verify] [--write-checklist] [--completeness]` | Per-entry deploy report — origin · surfaces · in-sync/stale/never · **certification** (verified/attested/unverifiable/out-of-band-drift, §19.5) + the derived **publication truth** per surface (§20.1, #242) + ledger write paths (D18, §19) | ✅ |
|
|
67
102
|
| `pasco presence [--json]` / `pasco release plan [<id>] [--json]` | **Presence board** (D23/§22, #262): every surface a release has addressed, at its newest release, with the ledger-derived state (`in-sync`/`stale`/`never`), and the **release plan** — the ordered fan-out (`repo-meta` → `profile` → `distribution`, then slug), each surface's state and the per-kind **factual spine**. Copy per channel lives in the shipped skills, not the engine | ✅ v1 |
|
|
68
103
|
| `pasco refine <platform> [--lang] [--variants N] [--choose <id>]` | Agent-driven variant flow (D10/D11/D13); the chosen body is `profiles/<platform>/variants/<id>.md` (a complete platform artifact), rendered by `pasco build` (#238) | 🚧 template + `--choose` (full flow M3) |
|
|
69
|
-
| `pasco check [platform]` |
|
|
70
|
-
| `pasco discover <url>... [--json] [--allow-host <hosts>]` | **Provisional profile** from public surfaces (#329, SPEC §7.10), for a prospect with no workspace: reads schema.org JSON-LD + links only (no prose mining), attaches per-fact provenance (`attested: false`) and lists `absent` fields; reports the audit's class names with discovery's own predicates (SPEC §7.10); closed host allowlist checked before any request (sequential, no retries, no credentials)
|
|
104
|
+
| `pasco check [platform]` | The workspace's checks declared in `content/` (schema-validated catalog) — or the conservative caps shipped with pasco when it declares none (#240) — + registry rules (M7/§18.8) + media-transport rules (M8/§18.6) + offline `max-chars`/`min-chars` over headlines/bios (#240) + `fact-present` (the built surface's fact tokens vs the SSOT render, #283) | 🚧 offline rules today (limits + registry + media + fact-present; remaining catalog M3/M4) |
|
|
105
|
+
| `pasco discover <url>... [--json] [--allow-host <hosts>] [--observations <file>]` | **Provisional profile** from public surfaces (#329, SPEC §7.10), for a prospect with no workspace: reads schema.org JSON-LD + links only (no prose mining), attaches per-fact provenance (`attested: false`) and lists `absent` fields; reports the audit's class names with discovery's own predicates (SPEC §7.10); closed host allowlist checked before any request (sequential, no retries, no credentials). `--observations` takes the rows a human read by hand (`surface`, `field` = `name`/`link`/`experience`, `value`, `url`, `retrieved_at`) for surfaces the machine cannot read (LinkedIn 999, no `Person` node): no request is made, the rows enter the same provisional profile/findings, and `--json` (payload **v2**: `via`, nullable `status`) marks each surface `via: fetch|manual`; a disagreement between two surfaces is a `contradiction` (two different names included). **Writes nothing** | ✅ v1 |
|
|
106
|
+
| `pasco achievements [--targets] [--json]` | The **career impact log** (D24/§23, #446): one row per result — `metric` (`value`/`target`/`baseline`/`unit`/`direction`) or `milestone` — with the units it belongs to (`refs`), how the result is known (`basis`) and its required `visibility`. The status is **DERIVED** at read time (`planned` · `measured` · `achieved` · `partial`), never stored; `--targets` prints only the planned goals; `--json` emits `{ schemaVersion: "1", scope, achievements }`. Read-only | ✅ v1 |
|
|
71
107
|
| `pasco audit [--json]` | Offline **diagnostic projection**, findings only (no score/prioritisation/branding, #283): three fixed classes — `dangling-ref` FAIL · `contradiction` FAIL · `unsourced-claim` WARN — exit 1 iff any FAIL; `--json` carries `{ schemaVersion: "1", scope: { declared, checked, notRun }, findings }` | ✅ v1 |
|
|
72
108
|
| `pasco entity [--json \| --jsonld [--out <file>] [--bare]]` | The **entity projection** for surfaces the engine does not render (#287): `--json` is the frozen view — `entity { id, type, name, sameAs, email }`, the structured-data `gaps` (geoCheck codes verbatim), `jsonld: null` and `faqJsonLd` (the `FAQPage` node, `null` when the workspace declares no `faq[]`); `--jsonld` is the **same assembler output** the renderers embed, as paste-ready `<script type="application/ld+json">` blocks — the entity first, then the `FAQPage` when it exists (#360). Exit 1 when `@id` is absent (never invented) | ✅ v1 |
|
|
73
109
|
| `pasco cite-check [<answers.ndjson>] [--json]` | The **citation matcher** over saved answer-engine answers (#291): reads the runner's NDJSON (stdin when no path), matches the workspace's declared entity and emits, **per answer and never aggregated**, `{ cited, confidence, spans, reasons }`. Ladder: `citation-url` (a citation on the entity's own page or its `sameAs` set — host, and the declared path or a descendant) and `exact-name` → **high**; `alias` (`identity.orgName`) and `handle` → **medium**; `partial` (any contiguous multi-token prefix, ≥ 2 tokens) → **low**, and low is never citable downstream. Refuses to guess: a one-word name with no declared disambiguator (`cited: false`, the longer capitalised reading named as `competing`), or a contrast mention, is not a citation; a declared qualifier or an adjacent capital never disqualifies a hit by itself | ✅ v1 |
|
|
74
110
|
| `pasco export ai-policy [--out <dir> \| --check] [--lang <l>]` | The M9 stance as an artifact (#290): `--out` writes `robots.txt` (**two named groups, no wildcard** — the listed retrieval crawlers allowed, the listed training crawlers disallowed, each UA commented with its registry entry/operator/source/date; the file declares that a crawler it does not name is **not restricted**), `llms.txt` (the workspace index: name, definition, canonical identity, `faq[]`, `publications[]`, with a coverage assertion) and `AI-POLICY.md` (allowed · blocked · that same explicit-list limit · who can change it · the date, quoting the stance verbatim). `--check` exits 1 on an incomplete or unsourced entry, a duplicate token (case-insensitive), a malformed/future/`>180`-day `as_of`, or a `policy.as_of` that is stale or older than its newest row; `--out` refuses to write one. The UA list is projected from the platform registry — never hardcoded in the CLI | ✅ v1 |
|
|
75
111
|
| `pasco export profile [--json] [--now <iso8601>]` | The **normalized profile** for programmatic consumers (#348): `{ schemaVersion, engine, source: { commit, generatedAt }, profile }` — `profile` is exactly what `core.load()` returns (**every root the workspace declares, plus every list root defaulted to `[]`**; an optional object root appears only when declared), never a platform slice (`compile()` makes that other object). `artifactRefs` ids resolve in `profile.artifacts[]`, `publications[].url` as declared, `deployments` included. No clock is read: `generatedAt` is `null` unless `--now` pins it, so two runs are byte-identical; `source.commit` is the workspace git HEAD (`null` outside a repo) | ✅ v1 |
|
|
76
|
-
| `pasco post <seed\|task\|choose\|check\|export\|import\|list>` | Post experiment (#264/#265, long-form #266): a seed at `posts/<seed>/` → task bundle → chosen variant → check → Postiz draft payload (API channels) or paste checklist (Substack/Ghost). Two seed formats (`short`/`longform`); a short child derived from an essay records `derivedFrom`. The CLI never calls an LLM | 🚧 experiment (not in the v1 contract;
|
|
112
|
+
| `pasco post <seed\|task\|choose\|check\|export\|import\|list>` | Post experiment (#264/#265, long-form #266): a seed at `posts/<seed>/` → task bundle → chosen variant → check → Postiz draft payload (API channels) or paste checklist (Substack/Ghost). Two seed formats (`short`/`longform`); a short child derived from an essay records `derivedFrom`. The CLI never calls an LLM | 🚧 experiment (not in the v1 contract; [the post experiment notes](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/post-mvp.md)) |
|
|
77
113
|
| `pasco skills:export <dir> [--force] \| --list` | Copy the platform skills this package ships into a directory you name (`--list` shows them; no per-agent paths are invented — #254). A destination and `--list` are **separate invocations**: both together is usage (exit 2), never an ignored argument | ✅ |
|
|
78
114
|
|
|
79
115
|
The partial commands are explicit, and each one says what still owns the rest:
|
|
@@ -83,11 +119,12 @@ The partial commands are explicit, and each one says what still owns the rest:
|
|
|
83
119
|
`--choose` records the pick in `content/active.yaml`. The platform skill ships
|
|
84
120
|
**inside this package**, so the template prints its real path on an npm install
|
|
85
121
|
(and says `no skill for <platform>` vs `skills not shipped` distinctly —
|
|
86
|
-
#253). The full variant flow is M3
|
|
122
|
+
#253). The full variant flow is M3.
|
|
87
123
|
- **`check`** — executes the offline rules that exist today: the brand handle
|
|
88
124
|
matrix (brand flavor only, D15/§17), the evidence-registry checks (M7/§18.8),
|
|
89
125
|
the media-transport rules (M8/§18.6), the `max-chars` / `min-chars` rows
|
|
90
|
-
declared in `content
|
|
126
|
+
declared in the workspace's `content/` (or the package's conservative caps when
|
|
127
|
+
it declares none) over the headline/bio rows that exist
|
|
91
128
|
(offline, warn-class, #240), and **`fact-present`** — for the surfaces whose
|
|
92
129
|
catalog declares it (`content/checks.yaml` ∪ the shipped
|
|
93
130
|
`skills/<platform>/checks.yaml`): the artifact `pasco build` last wrote is
|
|
@@ -97,13 +134,12 @@ The partial commands are explicit, and each one says what still owns the rest:
|
|
|
97
134
|
surface that disagrees with the SSOT is a fact problem, not a schema one. A
|
|
98
135
|
targeted surface with no artifact reports the rule as **not run**, never as OK. The remaining
|
|
99
136
|
per-platform rules in the catalog ship with the skills `checks.yaml` union (M3)
|
|
100
|
-
and the full `pasco check` (M4).
|
|
101
|
-
(`src/commands/check.ts`).
|
|
137
|
+
and the full `pasco check` (M4).
|
|
102
138
|
- **`skills:export`** — copies the platform skills this package ships into the
|
|
103
139
|
directory the user names (`--list` first; an existing non-empty directory needs
|
|
104
140
|
`--force`). It replaces the earlier projection stub: the CLI never invents a
|
|
105
141
|
per-agent destination and never points at tooling the reader cannot open
|
|
106
|
-
(
|
|
142
|
+
(#254).
|
|
107
143
|
- **`post`** — the post experiment (#264/#265, long-form #266): a CLI
|
|
108
144
|
subcommand family over the `posts/` store; it never calls an LLM, emits the
|
|
109
145
|
agent task, records the choice, checks limits/URLs/refs (plus title/dek and
|
|
@@ -112,7 +148,7 @@ The partial commands are explicit, and each one says what still owns the rest:
|
|
|
112
148
|
short child derived from an essay records `derivedFrom: <essay-id>`. It reads
|
|
113
149
|
the `skills/` bundled with the package (set `PASCO_SKILLS_DIR` only to point
|
|
114
150
|
at a custom tree), and it is **not part of the v1 contract yet**
|
|
115
|
-
(
|
|
151
|
+
([the post experiment notes](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/post-mvp.md)).
|
|
116
152
|
|
|
117
153
|
### The audit (`pasco audit --json`, #283)
|
|
118
154
|
|
|
@@ -170,7 +206,7 @@ a usage error, and a schema/load failure is the standard error envelope at exit
|
|
|
170
206
|
`content/deployments.yaml` (the ledger is not a fact source) — it works on a
|
|
171
207
|
workspace that has neither a ledger nor a built artifact.
|
|
172
208
|
|
|
173
|
-
What it composes (
|
|
209
|
+
What it composes ([SPEC §7.1](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/SPEC.md)): `load`/schema, `reconciledDrift` (drift
|
|
174
210
|
plus the #238 variant-store reconciliation `validate` applies), `geoCheck` (its
|
|
175
211
|
`faq-coverage` premise gates the claim rows; the codes themselves map to no class),
|
|
176
212
|
the completeness `evidence` premise, and `fact-present`.
|
|
@@ -187,7 +223,7 @@ warns about) and `geoCheck`'s coverage codes.
|
|
|
187
223
|
|
|
188
224
|
`audit` diagnoses; `entity` is the artifact for a surface this engine does not
|
|
189
225
|
render (WordPress, Webflow, Next, a landing page). It exposes the **same** entity
|
|
190
|
-
the renderers embed —
|
|
226
|
+
the renderers embed — the engine's single assembler, so there
|
|
191
227
|
is no CLI-local builder and no second truth (SPEC §7.2).
|
|
192
228
|
|
|
193
229
|
```sh
|
|
@@ -400,13 +436,13 @@ is frozen in SPEC §7.5.
|
|
|
400
436
|
|
|
401
437
|
**A consumer that only needs to validate** does not need this package at all: the
|
|
402
438
|
released schema is published as a tag-pinned URL + sha256 in
|
|
403
|
-
[
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
[
|
|
439
|
+
[the consumer contract](https://github.com/ChristianLuciani/profile-as-code/blob/main/schema/consumer-contract.json).
|
|
440
|
+
This CLI releases schema **v1.16** at tag `v0.4.0`, so the tag-pinned artifact is
|
|
441
|
+
`https://raw.githubusercontent.com/ChristianLuciani/profile-as-code/v0.4.0/schema/profile.schema.json`
|
|
442
|
+
(the tag is the release trigger, so it exists before this tarball is published). A
|
|
443
|
+
consumer that needs `achievements[]` reads that tag; one that does not can keep pinning
|
|
444
|
+
`v0.3.7` unchanged. The reading guide is
|
|
445
|
+
[the schema contract reading guide](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/SCHEMA-CONTRACT.md) —
|
|
410
446
|
including what structural validation does **not** cover (the cross-file
|
|
411
447
|
invariants core re-checks, engine behavior, `.pascorc` and this payload's
|
|
412
448
|
envelope).
|
|
@@ -477,7 +513,10 @@ pasco edit positioning.statement.en # the current value
|
|
|
477
513
|
pasco edit positioning.statement.en --set "…" --check # the published lines this edit changes
|
|
478
514
|
pasco edit positioning.statement.en --set "…" # write it (one line, comments intact)
|
|
479
515
|
pasco edit headlines[platform=github-readme,lang=en].text --set "…" --check
|
|
516
|
+
pasco edit identity.definition.en --set "…" # the canonical definition
|
|
517
|
+
pasco edit 'faq[id=what-is-elena,lang=en].a' --set "…" # one answer
|
|
480
518
|
pasco edit identity.links[platform=github].url # read-only (the URL SSOT)
|
|
519
|
+
pasco edit --create faq # seed a missing root (content/faq.yaml)
|
|
481
520
|
```
|
|
482
521
|
|
|
483
522
|
Addressable fields (by **contract path**, never by line/offset — the schema is the
|
|
@@ -488,8 +527,51 @@ authority on what a field is):
|
|
|
488
527
|
| `positioning.statement.<lang>` | `positioning.statement[lang]` (`lang`: `en`/`es`/`fr`) |
|
|
489
528
|
| `headlines[platform=<p>,lang=<l>].text` | the matching `headlines[]` row |
|
|
490
529
|
| `bios[platform=<p>,lang=<l>,length=<len>].text` | the matching `bios[]` row |
|
|
530
|
+
| `identity.definition.<lang>` | the canonical per-language definition (#288, v1.10) |
|
|
531
|
+
| `faq[id=<slug>,lang=<l>].q` / `.a` | one `faq[]` row's question/answer for that language — `q`/`a` are langMaps, so `lang` is **required** |
|
|
532
|
+
| `faq[id=<slug>].source` | the row's optional http(s) pointer (`lang` does not apply) |
|
|
491
533
|
| `identity.links[platform=<p>,label=<l>].url` | the row's URL — **read-only**; `label` disambiguates two links on one platform |
|
|
492
534
|
|
|
535
|
+
`faq[]` is selected by its stable `id`, never by array index: a repeated id matches
|
|
536
|
+
more than one row and the command refuses to choose, exactly like a duplicate
|
|
537
|
+
bio. An optional field the row does not carry (`source`, or `q.es`) is **0 rows** —
|
|
538
|
+
`edit` never creates it; the row itself must already exist.
|
|
539
|
+
|
|
540
|
+
**Finding the path (`--list`, #411).** `pasco edit --list` prints every
|
|
541
|
+
addressable row of the loaded workspace — read-only and deterministic, built by
|
|
542
|
+
the same constructors resolution uses, never a second grammar:
|
|
543
|
+
|
|
544
|
+
```sh
|
|
545
|
+
pasco edit --list # every block, one address per line
|
|
546
|
+
pasco edit --list headlines # scope: positioning | headlines | bios | identity.definition | identity.links | faq
|
|
547
|
+
pasco edit --list --json # { addresses: [{ address, kind, readOnly }], warnings }
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Link rows are marked `(read-only)`. A link row whose platform cannot be
|
|
551
|
+
expressed as a selector (empty, padded, or carrying `,`, `]`, `=`, `"` or a
|
|
552
|
+
control character) is a **warning**; a **label** the grammar cannot express is
|
|
553
|
+
dropped and the row is listed by its platform when that resolves to one row (the
|
|
554
|
+
warning says so). Any address that resolves to **more than one row** (a duplicate
|
|
555
|
+
headline/bio, a label-less link sharing its platform) is a warning too: the list
|
|
556
|
+
advertises only paths that resolve to exactly one row, the bar `edit` enforces. `--list` never locates a CST
|
|
557
|
+
node, never takes `content/.pasco-edit.lock` and writes nothing; an empty
|
|
558
|
+
workspace prints the header alone (exit 0). Combining `--list` with `--set`,
|
|
559
|
+
`--check`, `--create` or an address is usage (exit 2), never a silently ignored
|
|
560
|
+
argument.
|
|
561
|
+
|
|
562
|
+
**Creating a missing root (`--create`, #435).** The splice never invents bytes, so
|
|
563
|
+
it cannot create `content/faq.yaml` — the answer-engine surface the product
|
|
564
|
+
exists to be quoted through. `--create <root>` is a **different operation**, not
|
|
565
|
+
a rewrite of the writer: it writes a schema-valid **template** for a root the
|
|
566
|
+
workspace does not declare yet, refuses when the root already exists (idempotent:
|
|
567
|
+
nothing is written, exit 1), and validates the result through the same `load()`
|
|
568
|
+
gate over a temp copy before the file reaches disk. It runs the same cap
|
|
569
|
+
pre-flight as `--set`, and `--check` previews the seed (validates it, writes
|
|
570
|
+
nothing). Both the root name and the file form are accepted:
|
|
571
|
+
`pasco edit --create faq` / `pasco edit --create content/faq.yaml`. Seedable
|
|
572
|
+
roots today: `faq` (3 placeholder pairs — the schema minimum). Every value is a
|
|
573
|
+
`<placeholder>` the owner replaces: the engine never invents facts (D11/D14).
|
|
574
|
+
|
|
493
575
|
**The write (S2, #249).** A byte-range splice over the **original text**: the CST
|
|
494
576
|
locates the scalar and only those bytes are replaced, so comments, alignment and
|
|
495
577
|
layout survive *by construction* and the diff is **one line** (a document
|
|
@@ -504,7 +586,8 @@ command resolved — a row inserted or reordered since the address was resolved
|
|
|
504
586
|
a **staleness** refusal (exit 1), not a silent write to the wrong row.
|
|
505
587
|
Concurrent `pasco edit` writers on one workspace serialize on
|
|
506
588
|
`content/.pasco-edit.lock` (a second waits 2 s, then refuses; a lock left by a
|
|
507
|
-
crash is broken after 30 s
|
|
589
|
+
crash is broken after 30 s — `--create` takes the same lock and re-checks the
|
|
590
|
+
root immediately before its exclusive create); an external editor is caught by the re-read
|
|
508
591
|
immediately before the atomic rename. A symlinked
|
|
509
592
|
content file keeps its link (the target is replaced) and the file mode is
|
|
510
593
|
preserved. Re-setting an identical value is a **noop** (exit 0, nothing written).
|
|
@@ -518,7 +601,11 @@ it is honest where echoing the addressed row would not be: a paste surface
|
|
|
518
601
|
publishes exactly **one** bio (length priority `long > medium > short`), several
|
|
519
602
|
renderers never emit the statement, and a renderer-less surface carries no
|
|
520
603
|
headline. In those cases the artifact does not change and the preview says so —
|
|
521
|
-
with the reason, and exit `1`.
|
|
604
|
+
with the reason, and exit `1`. Two #435 fields have a different published
|
|
605
|
+
surface: `identity.definition` reaches the entity JSON-LD that `github-readme`
|
|
606
|
+
and `github-org` embed (the preview shows that block), and `faq[]` has **no**
|
|
607
|
+
`pasco build` artifact — its published surface is the `FAQPage` node
|
|
608
|
+
`pasco entity --jsonld` emits (#360), so `--check` previews that node's bytes. It runs the writer's **eligibility and staleness
|
|
522
609
|
checks first**, so `--check` refuses the same ADDRESSED shapes `--set` refuses
|
|
523
610
|
(block/multi-line/anchored scalars, a stale row, a read-only **file**). A
|
|
524
611
|
read-only **directory**, a validation refusal or a concurrent change can still
|
|
@@ -557,7 +644,12 @@ when the workspace declares checks, the **sourced caps shipped with pasco**
|
|
|
557
644
|
otherwise — and prints the cap, its **cited source** and its provenance tier. Over-cap is a
|
|
558
645
|
**warning**, never a block (D12: facts warn, schema fails): `edit` says so, `check`
|
|
559
646
|
reports the same finding (same executor), and a platform with **no sourced cap**
|
|
560
|
-
says `cap
|
|
647
|
+
says `cap unknown` instead of guessing a number. The answer-engine fields are
|
|
648
|
+
pre-flighted the same way against the **contract's** own caps (#435): 160 chars
|
|
649
|
+
per `identity.definition` value, 90 per `faq[].q`, 320 per `faq[].a` (v1.10,
|
|
650
|
+
#288, `[OFICIAL]`). Those are schema `maxLength`s, so over one the pre-flight
|
|
651
|
+
warns and the **write is refused by validation** — the one place the cap is a
|
|
652
|
+
failure, not a warning (D12).
|
|
561
653
|
|
|
562
654
|
`--json` payload (inside the CLI's usual envelope, which also carries `command`,
|
|
563
655
|
`ok` and `exitCode`): `{ address, current, proposed, rendered, ok, noop, written,
|
|
@@ -568,14 +660,17 @@ record; `null` for a read-only link or an address that is not one row), and
|
|
|
568
660
|
`noop` says the value was already there, `written` says the file changed, and
|
|
569
661
|
`warnings` carries the non-blocking findings a write proceeded through (a D22/§21
|
|
570
662
|
selection error that makes `pasco build` refuse the workspace — the SSOT write is
|
|
571
|
-
still valid, so it is reported, not blocked).
|
|
572
|
-
(
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
663
|
+
still valid, so it is reported, not blocked). `--create` carries its own payload
|
|
664
|
+
(`{ mode: "create", root, file, created, preview, seed, limits, warnings }`). Exit
|
|
665
|
+
codes: **0** the write succeeded (or the noop was a noop), the preview changes the
|
|
666
|
+
artifact, or the seed was written/previewed · **1** the address did not resolve to
|
|
667
|
+
exactly one row, the target is read-only or changed under the command (stale), the
|
|
668
|
+
validation refused the result, `--create` found the root already present (nothing
|
|
669
|
+
written), or (preview only) the artifact does not change or a D22/§21 selection
|
|
670
|
+
error makes `build` refuse the workspace (`kind: "selection"`) · **2** usage
|
|
671
|
+
(malformed address, read-only field, block/multi-line scalar, anchored/aliased
|
|
672
|
+
scalar, a value with a line break, `--check` without `--set`, `--create` with an
|
|
673
|
+
unknown root or next to `--set`/`--list`/an address).
|
|
579
674
|
|
|
580
675
|
### The machine contract (`--json`, S5 / #252)
|
|
581
676
|
|
|
@@ -595,7 +690,7 @@ are the envelope; the edit's own fields are
|
|
|
595
690
|
| `stale` | `Record<surface, "in-sync" \| "stale">` | each **recorded** surface of the edited unit |
|
|
596
691
|
| `state` | `"never" \| "in-sync" \| "stale" \| null` | the §19 unit rollup (`null` when the address is not one writable unit) |
|
|
597
692
|
| `drift` | number | the measured drift-check count |
|
|
598
|
-
| `limit` | `{cap, source, tier} \| null` | the applicable published cap; `null` when
|
|
693
|
+
| `limit` | `{cap, source, tier} \| null` | the applicable published cap (the catalog's for headline/bio, the contract's for definition/faq); `null` when there is none **or** the address type has no text cap (a statement, a link, `faq[].source`) |
|
|
599
694
|
| `over` | boolean | the value exceeds that cap |
|
|
600
695
|
| `variant` | string \| null | the active #238 variant shadowing the **addressed platform** (a statement names its variants in `warnings` instead) |
|
|
601
696
|
| `reason` | string \| null | why the preview does not reach the artifact (the same sentence the human output prints as `why:`) |
|
|
@@ -616,9 +711,9 @@ finding in `warnings` (the SSOT write is valid even when `build` refuses).
|
|
|
616
711
|
nobody edits a field the renderer ignores.
|
|
617
712
|
|
|
618
713
|
Exit codes (one row per user-visible state; the CLI rows are pinned in
|
|
619
|
-
|
|
714
|
+
[the edit command tests](https://github.com/ChristianLuciani/profile-as-code/blob/main/packages/cli/tests/edit.test.ts), the write-path rows — stale, read-only,
|
|
620
715
|
validation, lock contention — at the writer seam in
|
|
621
|
-
|
|
716
|
+
[the edit write tests](https://github.com/ChristianLuciani/profile-as-code/blob/main/packages/cli/tests/edit-write.test.ts)):
|
|
622
717
|
|
|
623
718
|
| Code | When |
|
|
624
719
|
|---|---|
|
|
@@ -666,8 +761,9 @@ It is **derived and never stored**: `--json` carries it as the additive
|
|
|
666
761
|
`publication` array, `--completeness` folds it in as *coverage of publication*
|
|
667
762
|
(the weighted percentage never moves because a surface drifted), and
|
|
668
763
|
`--write-checklist` writes the coverage phrase into the generated `CHECKLIST.md`.
|
|
669
|
-
The
|
|
670
|
-
axes) and
|
|
764
|
+
The resolution lives in [the publication module](https://github.com/ChristianLuciani/profile-as-code/blob/main/packages/cli/src/publication.ts)
|
|
765
|
+
(one home for artifact resolution and the three axes), and the cap executor `check`
|
|
766
|
+
and `status` share lives in [the limits module](https://github.com/ChristianLuciani/profile-as-code/blob/main/packages/cli/src/limits.ts).
|
|
671
767
|
|
|
672
768
|
## Usage
|
|
673
769
|
|
|
@@ -687,7 +783,7 @@ pasco status --write-checklist # regenerate CHECKLIST.md from the ledger
|
|
|
687
783
|
pasco status # …also prints the derived publication-truth line:
|
|
688
784
|
# github-readme: in-sync · artifact: matches-SSOT · limits: ok
|
|
689
785
|
pasco refine linkedin --variants 3 # emits the agent task template (full flow M3)
|
|
690
|
-
pasco refine linkedin --choose
|
|
786
|
+
pasco refine linkedin --choose linkedin-en-2 # records the chosen variant in content/active.yaml
|
|
691
787
|
pasco check github-readme
|
|
692
788
|
pasco audit # findings only: dangling-ref · contradiction · unsourced-claim
|
|
693
789
|
pasco audit --json # { schemaVersion, findings } — exit 1 iff any FAIL
|
|
@@ -783,7 +879,6 @@ script, no nag on every command:
|
|
|
783
879
|
- [Patreon](https://www.patreon.com/cluciani)
|
|
784
880
|
- [GitHub Sponsors](https://github.com/sponsors/ChristianLuciani)
|
|
785
881
|
|
|
786
|
-
A tip funds the engine
|
|
787
|
-
(`docs/SPEC.md`), the renderers and the platform skills. It buys **no** SLA, no
|
|
882
|
+
A tip funds the engine and this CLI — the [schema contract](https://github.com/ChristianLuciani/profile-as-code/blob/main/docs/SPEC.md), the renderers and the platform skills. It buys **no** SLA, no
|
|
788
883
|
priority handling, and no influence over the roadmap: a proposal is weighed on
|
|
789
884
|
its merits, in public. The code stays MIT — it is yours whether you tip or not.
|