@profile-as-code/core 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +80 -2
- package/dist/byte-order.d.ts +17 -0
- package/dist/byte-order.d.ts.map +1 -0
- package/dist/byte-order.js +19 -0
- package/dist/byte-order.js.map +1 -0
- package/dist/compile.d.ts +40 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +120 -0
- package/dist/compile.js.map +1 -0
- package/dist/completeness.d.ts +81 -0
- package/dist/completeness.d.ts.map +1 -0
- package/dist/completeness.js +300 -0
- package/dist/completeness.js.map +1 -0
- package/dist/drift.d.ts +43 -0
- package/dist/drift.d.ts.map +1 -0
- package/dist/drift.js +277 -0
- package/dist/drift.js.map +1 -0
- package/dist/generated/platforms.gen.d.ts +26 -0
- package/dist/generated/platforms.gen.d.ts.map +1 -0
- package/dist/generated/platforms.gen.js +185 -0
- package/dist/generated/platforms.gen.js.map +1 -0
- package/dist/generated/schema.zod.d.ts +575 -0
- package/dist/generated/schema.zod.d.ts.map +1 -0
- package/dist/generated/schema.zod.js +31 -0
- package/dist/generated/schema.zod.js.map +1 -0
- package/dist/geo.d.ts +34 -0
- package/dist/geo.d.ts.map +1 -0
- package/dist/geo.js +601 -0
- package/dist/geo.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger.d.ts +238 -0
- package/dist/ledger.d.ts.map +1 -0
- package/dist/ledger.js +429 -0
- package/dist/ledger.js.map +1 -0
- package/dist/load.d.ts +51 -0
- package/dist/load.d.ts.map +1 -0
- package/dist/load.js +129 -0
- package/dist/load.js.map +1 -0
- package/dist/media.d.ts +82 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +170 -0
- package/dist/media.js.map +1 -0
- package/dist/paste-source.d.ts +14 -0
- package/dist/paste-source.d.ts.map +1 -0
- package/dist/paste-source.js +17 -0
- package/dist/paste-source.js.map +1 -0
- package/dist/registry.d.ts +76 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +210 -0
- package/dist/registry.js.map +1 -0
- package/dist/selection.d.ts +98 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +242 -0
- package/dist/selection.js.map +1 -0
- package/dist/structured-data.d.ts +82 -0
- package/dist/structured-data.d.ts.map +1 -0
- package/dist/structured-data.js +265 -0
- package/dist/structured-data.js.map +1 -0
- package/dist/types.d.ts +320 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +34 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christian Luciani
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,81 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @profile-as-code/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The core engine of **profile-as-code** (M1): load a profile-data workspace,
|
|
4
|
+
validate it against the contract, and run the drift / GEO / compile layers.
|
|
5
|
+
|
|
6
|
+
The contract source is `schema/profile.schema.json` (D3) — Zod schemas are
|
|
7
|
+
**generated** from it, never hand-written (see `scripts/gen-zod.ts`).
|
|
8
|
+
|
|
9
|
+
## API
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { load, driftCheck, geoCheck, compile } from '@profile-as-code/core';
|
|
13
|
+
|
|
14
|
+
const profile = load('/path/to/profile-data'); // parse content/*.yaml + validate (FAIL on schema, D12)
|
|
15
|
+
const issues = driftCheck(profile); // Issue[] — WARN only, never throws (D12)
|
|
16
|
+
const geo = geoCheck(profile); // GeoReport — 0..100 score, not a gate (§8)
|
|
17
|
+
const slice = compile(profile, 'linkedin', 'es'); // PlatformProfile — language-aware renderer slice
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **`load(workspace)`** — reads `content/*.yaml` (file name = root property:
|
|
21
|
+
`bios.yaml` → `bios[]`, `identity.yaml` → `identity`, …), merges, validates
|
|
22
|
+
against the generated Zod, normalizes (collections default to `[]`).
|
|
23
|
+
Throws `LoadError` (workspace problems) / `SchemaValidationError` (contract
|
|
24
|
+
violations with one precise `path` + `message` per error — the fail-on-schema
|
|
25
|
+
side of D12). Unknown files fail validation via `additionalProperties: false`.
|
|
26
|
+
- **`driftCheck(profile)`** — factual contradictions across surfaces, all WARN
|
|
27
|
+
(users may have different intents per platform, but they must SEE them):
|
|
28
|
+
uncanonicalized platform URLs, `start > end`, duplicated bios/headlines,
|
|
29
|
+
same role/org with different dates, same title with different year/venue,
|
|
30
|
+
ORCID conflicts/mismatches vs `identity.links`, active entries without a
|
|
31
|
+
curated base, unknown active platform keys, skill level conflicts.
|
|
32
|
+
*Out of scope (v1):* free-text mining of bio prose, DOI resolvability
|
|
33
|
+
(network), the variants store (only the platform+lang base is validated).
|
|
34
|
+
- **`geoCheck(profile)`** — weighted report (14 checks since v1.10, 0..100):
|
|
35
|
+
**fact consistency** (conflicts + per-platform bio/headline coverage),
|
|
36
|
+
**structured-data readiness** (`schema.org` Person JSON-LD with `sameAs`,
|
|
37
|
+
ready for the web renderer), **semantic associations** (the 3–5
|
|
38
|
+
`positioning.geo.semanticAssociations` anchors and how well each is
|
|
39
|
+
reinforced across statement/skills/keywords/projects), **citability**
|
|
40
|
+
(DOI/ORCID on publications, `evidence[]` on projects) plus the v1.10 citability
|
|
41
|
+
codes — canonical definition, FAQ coverage, freshness, sameAs coverage, name
|
|
42
|
+
collision. Missing data is neutral for the fact checks (never penalized);
|
|
43
|
+
readiness/citability checks (structured data, semantic anchors, the v1.10 codes)
|
|
44
|
+
can score 0 when the signal is absent — a reported gap, not a penalty (SPEC §8).
|
|
45
|
+
- **`compile(profile, platform, lang)`** — facts pass through untouched
|
|
46
|
+
(identical on every platform = GEO-citable); text (bios, headlines, statement,
|
|
47
|
+
audiences, citationsGoal) is filtered by platform+lang and falls back to `en`
|
|
48
|
+
(always required by the schema), recording each fallback in `fallbacks`.
|
|
49
|
+
|
|
50
|
+
## Contract fidelity (generator)
|
|
51
|
+
|
|
52
|
+
`scripts/gen-zod.ts` regenerates `src/generated/schema.zod.ts` from
|
|
53
|
+
`schema/profile.schema.json`. Because `json-schema-to-zod@2.8.1` does not resolve
|
|
54
|
+
`$defs`, ignore `propertyNames`, and bloats `oneOf`, the generator:
|
|
55
|
+
|
|
56
|
+
1. inlines `#/$defs/*` references (fails on unknown/circular defs),
|
|
57
|
+
2. rewrites the lone `oneOf` (`check.value`) to `anyOf` (clean `z.union`),
|
|
58
|
+
3. materializes `langMap.en` as a real property (keeps `required: ['en']`), and
|
|
59
|
+
4. wraps every langMap with a `langMapKeys` superRefine enforcing the
|
|
60
|
+
`en/es/fr` key enum (`propertyNames`).
|
|
61
|
+
|
|
62
|
+
Safety nets fail CI loudly if the output ever contains `z.any()` or loses the
|
|
63
|
+
langMap guard.
|
|
64
|
+
|
|
65
|
+
## Test
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
pnpm install
|
|
69
|
+
pnpm test # vitest (load, drift, geo, compile)
|
|
70
|
+
pnpm build # strict tsc → dist/
|
|
71
|
+
pnpm gen:zod # regenerate src/generated/schema.zod.ts (must produce no diff)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Fixtures live in `tests/fixtures/`: `minimal/` (drift-free, GEO-rich — the
|
|
75
|
+
reference fixture) and `drifting/` (schema-valid but contradicts itself on
|
|
76
|
+
every drift rule).
|
|
77
|
+
|
|
78
|
+
Requires Node ≥ 22 (the `engines` field of this manifest). Full source and docs
|
|
79
|
+
live in the [public repository](https://github.com/ChristianLuciani/profile-as-code) —
|
|
80
|
+
`scripts/gen-zod.ts` and `tests/fixtures/` above are package-relative paths there
|
|
81
|
+
(`packages/core/…`); the contract source is the repository's `schema/profile.schema.json`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* byte-order.ts — the ONE home of every text-order comparison in the engine
|
|
3
|
+
* (D19 byte-parity, issue #305).
|
|
4
|
+
*
|
|
5
|
+
* The comparator is UTF-16 code UNITS — JS native `a < b` string order:
|
|
6
|
+
* deterministic on every environment, ICU-free. It is NOT codepoint order on
|
|
7
|
+
* the astral edge, and SPEC §15.3 names that choice exactly. A `localeCompare`
|
|
8
|
+
* call-site would depend on the ICU build and can tie (`0`), leaking
|
|
9
|
+
* environment-specific order through a stable sort — the class #257 fixed for
|
|
10
|
+
* the featured slice and #305 closes workspace-wide.
|
|
11
|
+
*
|
|
12
|
+
* The discriminating probe (`apple` vs `APPLE`: ICU collation reads lowercase
|
|
13
|
+
* first, code units read uppercase first) lives at
|
|
14
|
+
* packages/core/tests/byte-order.test.ts.
|
|
15
|
+
*/
|
|
16
|
+
export declare function byteOrder(a: string, b: string): number;
|
|
17
|
+
//# sourceMappingURL=byte-order.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byte-order.d.ts","sourceRoot":"","sources":["../src/byte-order.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* byte-order.ts — the ONE home of every text-order comparison in the engine
|
|
3
|
+
* (D19 byte-parity, issue #305).
|
|
4
|
+
*
|
|
5
|
+
* The comparator is UTF-16 code UNITS — JS native `a < b` string order:
|
|
6
|
+
* deterministic on every environment, ICU-free. It is NOT codepoint order on
|
|
7
|
+
* the astral edge, and SPEC §15.3 names that choice exactly. A `localeCompare`
|
|
8
|
+
* call-site would depend on the ICU build and can tie (`0`), leaking
|
|
9
|
+
* environment-specific order through a stable sort — the class #257 fixed for
|
|
10
|
+
* the featured slice and #305 closes workspace-wide.
|
|
11
|
+
*
|
|
12
|
+
* The discriminating probe (`apple` vs `APPLE`: ICU collation reads lowercase
|
|
13
|
+
* first, code units read uppercase first) lives at
|
|
14
|
+
* packages/core/tests/byte-order.test.ts.
|
|
15
|
+
*/
|
|
16
|
+
export function byteOrder(a, b) {
|
|
17
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=byte-order.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byte-order.js","sourceRoot":"","sources":["../src/byte-order.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAC,CAAS,EAAE,CAAS;IAC5C,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* compile(profile, platform, lang) — the language-aware slice a renderer consumes
|
|
3
|
+
* (SPEC §7). Facts (identity, experience, projects, publications, skills) pass
|
|
4
|
+
* through UNTOUCHED — they are language-independent and identical on every
|
|
5
|
+
* platform (that is what makes them GEO-citable). Only text (bios, headlines,
|
|
6
|
+
* statement, audiences, citationsGoal) is filtered by platform+lang, falling back
|
|
7
|
+
* to `en` when the requested lang is absent (EN is always required by the schema).
|
|
8
|
+
*
|
|
9
|
+
* The platform set is the single `PLATFORMS` const from types.js (generated from
|
|
10
|
+
* the schema enum) — never duplicated here.
|
|
11
|
+
*/
|
|
12
|
+
import { type CompletenessPolicy } from "./completeness.js";
|
|
13
|
+
import { type SelectionPolicy } from "./selection.js";
|
|
14
|
+
import type { Lang, Platform, PlatformProfile, Profile } from "./types.js";
|
|
15
|
+
/**
|
|
16
|
+
* D22/§21: the compile policy — the completeness policy it always took, plus the
|
|
17
|
+
* optional selection policy. ADDITIVE: `CompletenessPolicy` callers keep
|
|
18
|
+
* compiling unchanged, and a policy without `selection` produces a slice
|
|
19
|
+
* byte-identical to before this feature existed (the compatibility guarantee,
|
|
20
|
+
* asserted by a golden test).
|
|
21
|
+
*/
|
|
22
|
+
export type CompilePolicy = CompletenessPolicy & {
|
|
23
|
+
selection?: SelectionPolicy;
|
|
24
|
+
/** D22/§21: the view NAME to resolve (a declared view may target a platform
|
|
25
|
+
* other than its own name, e.g. `--view film` → `github-readme`). Defaults to
|
|
26
|
+
* the platform, which is the implicit view (`pasco build linkedin`). */
|
|
27
|
+
view?: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Compile a PlatformProfile for one platform and language.
|
|
31
|
+
*
|
|
32
|
+
* @param policy the workspace compile policy (issue #102 + D22): the completeness
|
|
33
|
+
* policy, and optionally the fact-selection policy. The CLI passes `.pascorc`'s
|
|
34
|
+
* so the artifact carries the SAME number the workspace reports and the SAME
|
|
35
|
+
* view it resolved; omitted ⇒ no selection, shipped completeness default.
|
|
36
|
+
* @throws TypeError — `platform`/`lang` not in the contract enum (a renderer bug,
|
|
37
|
+
* not a user-data problem).
|
|
38
|
+
*/
|
|
39
|
+
export declare function compile(profile: Profile, platform: Platform, lang?: Lang, policy?: CompilePolicy): PlatformProfile;
|
|
40
|
+
//# sourceMappingURL=compile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,kBAAkB,EAAgB,MAAM,mBAAmB,CAAC;AAE1E,OAAO,EAAE,KAAK,eAAe,EAAe,MAAM,gBAAgB,CAAC;AAEnE,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAK3E;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,kBAAkB,GAAG;IAC/C,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;6EAEyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CACrB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,IAAI,GAAE,IAAW,EACjB,MAAM,CAAC,EAAE,aAAa,GACrB,eAAe,CAyGjB"}
|
package/dist/compile.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* compile(profile, platform, lang) — the language-aware slice a renderer consumes
|
|
3
|
+
* (SPEC §7). Facts (identity, experience, projects, publications, skills) pass
|
|
4
|
+
* through UNTOUCHED — they are language-independent and identical on every
|
|
5
|
+
* platform (that is what makes them GEO-citable). Only text (bios, headlines,
|
|
6
|
+
* statement, audiences, citationsGoal) is filtered by platform+lang, falling back
|
|
7
|
+
* to `en` when the requested lang is absent (EN is always required by the schema).
|
|
8
|
+
*
|
|
9
|
+
* The platform set is the single `PLATFORMS` const from types.js (generated from
|
|
10
|
+
* the schema enum) — never duplicated here.
|
|
11
|
+
*/
|
|
12
|
+
import { completeness } from "./completeness.js";
|
|
13
|
+
import { normalizeProfile } from "./load.js";
|
|
14
|
+
import { selectFacts } from "./selection.js";
|
|
15
|
+
import { structuredData } from "./structured-data.js";
|
|
16
|
+
import { PLATFORMS } from "./types.js";
|
|
17
|
+
const LANG_IDS = ["en", "es", "fr"];
|
|
18
|
+
/**
|
|
19
|
+
* Compile a PlatformProfile for one platform and language.
|
|
20
|
+
*
|
|
21
|
+
* @param policy the workspace compile policy (issue #102 + D22): the completeness
|
|
22
|
+
* policy, and optionally the fact-selection policy. The CLI passes `.pascorc`'s
|
|
23
|
+
* so the artifact carries the SAME number the workspace reports and the SAME
|
|
24
|
+
* view it resolved; omitted ⇒ no selection, shipped completeness default.
|
|
25
|
+
* @throws TypeError — `platform`/`lang` not in the contract enum (a renderer bug,
|
|
26
|
+
* not a user-data problem).
|
|
27
|
+
*/
|
|
28
|
+
export function compile(profile, platform, lang = "en", policy) {
|
|
29
|
+
if (!PLATFORMS.includes(platform)) {
|
|
30
|
+
throw new TypeError(`compile: unknown platform "${String(platform)}" (allowed: ${PLATFORMS.join(", ")})`);
|
|
31
|
+
}
|
|
32
|
+
if (!LANG_IDS.includes(lang)) {
|
|
33
|
+
throw new TypeError(`compile: unknown language "${String(lang)}" (allowed: ${LANG_IDS.join(", ")})`);
|
|
34
|
+
}
|
|
35
|
+
const fallbacks = [];
|
|
36
|
+
const langText = (langMap, path) => {
|
|
37
|
+
// Issue #84 (option (a) ratified): an empty-string value counts as ABSENT —
|
|
38
|
+
// nobody can display "" and a populated workspace is byte-identical; only
|
|
39
|
+
// previously-degenerate output changes. `|| undefined` (not just `??`) so
|
|
40
|
+
// emptiness falls through at BOTH lookup levels (lang-specific and en).
|
|
41
|
+
const direct = langMap?.[lang] || undefined;
|
|
42
|
+
if (direct !== undefined)
|
|
43
|
+
return direct;
|
|
44
|
+
const en = langMap?.en || undefined;
|
|
45
|
+
if (en !== undefined) {
|
|
46
|
+
fallbacks.push(path);
|
|
47
|
+
return en;
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
};
|
|
51
|
+
const positioning = profile.positioning;
|
|
52
|
+
const audiences = {};
|
|
53
|
+
for (const [name, map] of Object.entries(positioning?.audiences ?? {})) {
|
|
54
|
+
const text = langText(map, `positioning.audiences.${name}`);
|
|
55
|
+
if (text !== undefined)
|
|
56
|
+
audiences[name] = text;
|
|
57
|
+
}
|
|
58
|
+
// Brand blocks (v1.1/§17): text is lang-filtered (falling back to en), facts
|
|
59
|
+
// (urls, dates, status) pass through untouched.
|
|
60
|
+
const brandAudiences = {};
|
|
61
|
+
for (const [name, map] of Object.entries(profile.audiences ?? {})) {
|
|
62
|
+
const text = langText(map, `audiences.${name}`);
|
|
63
|
+
if (text !== undefined)
|
|
64
|
+
brandAudiences[name] = text;
|
|
65
|
+
}
|
|
66
|
+
const platformChecks = (profile.checks ?? []).filter((c) => c.platform === "all" || c.platform === platform);
|
|
67
|
+
// D22/§21 — the view. `facts` is what renderers may see; `profile` (unfiltered)
|
|
68
|
+
// is what completeness scores. The split is the contract, not an accident:
|
|
69
|
+
// - structuredData is built from the FILTERED profile, or the JSON-LD would
|
|
70
|
+
// claim entities the render never shows;
|
|
71
|
+
// - completeness stays on the UNFILTERED profile, because §20 scores the
|
|
72
|
+
// WORKSPACE, not a view (hiding half the SSOT must not lower the number).
|
|
73
|
+
const selection = policy?.selection === undefined
|
|
74
|
+
? undefined
|
|
75
|
+
: selectFacts(profile, policy.view ?? platform, platform, lang, policy.selection);
|
|
76
|
+
const facts = selection?.profile ?? profile;
|
|
77
|
+
return {
|
|
78
|
+
platform,
|
|
79
|
+
lang,
|
|
80
|
+
flavor: profile.flavor ?? "person",
|
|
81
|
+
// facts — untouched
|
|
82
|
+
identity: facts.identity,
|
|
83
|
+
experience: facts.experience ?? [],
|
|
84
|
+
projects: facts.projects ?? [],
|
|
85
|
+
publications: facts.publications ?? [],
|
|
86
|
+
skills: facts.skills ?? [],
|
|
87
|
+
socialHandles: profile.socialHandles ?? [],
|
|
88
|
+
newsletter: profile.newsletter,
|
|
89
|
+
podcast: profile.podcast,
|
|
90
|
+
press: profile.press ?? [],
|
|
91
|
+
brandAudiences,
|
|
92
|
+
// language-aware text
|
|
93
|
+
bios: (profile.bios ?? []).filter((b) => b.platform === platform && b.lang === lang),
|
|
94
|
+
headlines: (profile.headlines ?? []).filter((h) => h.platform === platform && h.lang === lang),
|
|
95
|
+
statement: langText(positioning?.statement, "positioning.statement"),
|
|
96
|
+
audiences,
|
|
97
|
+
citationsGoal: langText(positioning?.geo?.citationsGoal, "positioning.geo.citationsGoal"),
|
|
98
|
+
keywords: {
|
|
99
|
+
seo: profile.keywords?.seo?.[platform] ?? [],
|
|
100
|
+
geo: profile.keywords?.geo ?? {},
|
|
101
|
+
},
|
|
102
|
+
checks: platformChecks,
|
|
103
|
+
// Evidence registry (v1.4/D17, §18) — facts, pass through untouched.
|
|
104
|
+
credentials: facts.credentials ?? [],
|
|
105
|
+
artifacts: facts.artifacts ?? [],
|
|
106
|
+
...(profile.active?.[platform] ? { active: profile.active[platform] } : {}),
|
|
107
|
+
// v1.6 (#103): entity structured-data identity — assembled ONCE here, so no
|
|
108
|
+
// renderer re-derives `@id`/`sameAs` (SPEC §15.4, the §18.9 precedent).
|
|
109
|
+
structuredData: structuredData(facts, lang),
|
|
110
|
+
// Issue #102: the completeness report travels WITH the slice (like
|
|
111
|
+
// structuredData) so a renderer emits the number verbatim and never
|
|
112
|
+
// recomputes it. `policy` is the workspace's `.pascorc` policy when the CLI
|
|
113
|
+
// passes it, so the artifact and `pasco status --completeness` agree
|
|
114
|
+
// (review of #205, finding 3); omitted ⇒ the shipped default.
|
|
115
|
+
completeness: completeness(normalizeProfile(profile), policy),
|
|
116
|
+
...(selection === undefined ? {} : { selection: selection.report }),
|
|
117
|
+
fallbacks,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=compile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.js","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* completeness(profile, policy) — the profile-completeness checklist (issue #102).
|
|
3
|
+
*
|
|
4
|
+
* The engine computes; the workspace stores only the POLICY (weights, target,
|
|
5
|
+
* expected langs) and a GENERATED view — never the percentage (a derived number
|
|
6
|
+
* that would drift). Same contract as `geoCheck` (SPEC §7): pure, deterministic,
|
|
7
|
+
* offline, no network.
|
|
8
|
+
*
|
|
9
|
+
* Five signals, each an already-present engine fact (the set ratified 2026-10-07):
|
|
10
|
+
*
|
|
11
|
+
* presence blocks vs the flavor's expected set (flavor + top-level props)
|
|
12
|
+
* langCoverage en/es/fr coverage of every langMap (load/schema)
|
|
13
|
+
* geoAnchors semantic associations reinforced elsewhere (geoCheck's surface)
|
|
14
|
+
* evidence claims that carry a source + registry entries (`evidenceItem`)
|
|
15
|
+
* deployment share of trackable units with a ledger record (`ledger`)
|
|
16
|
+
*
|
|
17
|
+
* Policy is parameterized (dev-methodology §3): a user says "my target is the
|
|
18
|
+
* GitHub milestone set, not everything" by dropping an item (weight 0) — a
|
|
19
|
+
* parameter, never a code path. No weights are hardcoded outside the catalog.
|
|
20
|
+
*/
|
|
21
|
+
import type { LoadedProfile } from "./types.js";
|
|
22
|
+
/** One scored signal the engine can compute. */
|
|
23
|
+
export type CompletenessSignal = "presence" | "langCoverage" | "geoAnchors" | "evidence" | "deployment";
|
|
24
|
+
/** A catalog entry: the engine-owned definition of one completeness item. */
|
|
25
|
+
export interface CompletenessCatalogItem {
|
|
26
|
+
id: CompletenessSignal;
|
|
27
|
+
label: string;
|
|
28
|
+
/** The profile block(s) this item draws on — the issue's "source" column. */
|
|
29
|
+
block: string;
|
|
30
|
+
/** The shipped, documented default weight (normalized when the score is computed). */
|
|
31
|
+
weight: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The item catalog — the engine's knowledge of what "complete" can mean.
|
|
35
|
+
* `weight` here is the DEFAULT policy's weight; a policy overrides per id.
|
|
36
|
+
*/
|
|
37
|
+
export declare const COMPLETENESS_CATALOG: readonly CompletenessCatalogItem[];
|
|
38
|
+
/** The policy: what a workspace is being measured against. */
|
|
39
|
+
export interface CompletenessPolicy {
|
|
40
|
+
/** Name of the milestone this policy targets (informational, e.g. `github-milestone`). */
|
|
41
|
+
target?: string;
|
|
42
|
+
/** Languages text coverage is measured in (default: en/es/fr). */
|
|
43
|
+
langs?: readonly string[];
|
|
44
|
+
/** Per-item weight override; weight 0 removes the item from the score. */
|
|
45
|
+
items?: readonly {
|
|
46
|
+
id: string;
|
|
47
|
+
weight: number;
|
|
48
|
+
}[];
|
|
49
|
+
}
|
|
50
|
+
/** One computed item: a score in 0..1, its weight, and a short EN detail. */
|
|
51
|
+
export interface CompletenessItemResult {
|
|
52
|
+
id: string;
|
|
53
|
+
label: string;
|
|
54
|
+
block: string;
|
|
55
|
+
score: number;
|
|
56
|
+
weight: number;
|
|
57
|
+
satisfied: boolean;
|
|
58
|
+
detail: string;
|
|
59
|
+
}
|
|
60
|
+
export interface CompletenessReport {
|
|
61
|
+
/** 0..100, weighted mean of the item scores (one decimal, byte-stable). */
|
|
62
|
+
percent: number;
|
|
63
|
+
/** Items with score 1 — the numerator of the `11/18` surface. */
|
|
64
|
+
satisfied: number;
|
|
65
|
+
/** Items considered by the policy (weight > 0). */
|
|
66
|
+
total: number;
|
|
67
|
+
target: string;
|
|
68
|
+
items: CompletenessItemResult[];
|
|
69
|
+
}
|
|
70
|
+
/** The shipped, documented default (overridable per workspace via `.pascorc`). */
|
|
71
|
+
export declare const DEFAULT_COMPLETENESS_POLICY: CompletenessPolicy;
|
|
72
|
+
/**
|
|
73
|
+
* Compute the profile's completeness against a policy.
|
|
74
|
+
*
|
|
75
|
+
* @param profile a `load()`ed (normalized) profile — array blocks are defined.
|
|
76
|
+
* @param policy weights/target/langs; omitted ⇒ {@link DEFAULT_COMPLETENESS_POLICY}.
|
|
77
|
+
*/
|
|
78
|
+
export declare function completeness(profile: LoadedProfile, policy?: CompletenessPolicy): CompletenessReport;
|
|
79
|
+
/** `62.5% — 3/5` — the deterministic one-liner the CLI and the renderer share. */
|
|
80
|
+
export declare function completenessLine(report: CompletenessReport): string;
|
|
81
|
+
//# sourceMappingURL=completeness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completeness.d.ts","sourceRoot":"","sources":["../src/completeness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,gDAAgD;AAChD,MAAM,MAAM,kBAAkB,GAC1B,UAAU,GACV,cAAc,GACd,YAAY,GACZ,UAAU,GACV,YAAY,CAAC;AAEjB,6EAA6E;AAC7E,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,kBAAkB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,uBAAuB,EAMzD,CAAC;AAEX,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,0EAA0E;IAC1E,KAAK,CAAC,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACnD;AAED,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,sBAAsB,EAAE,CAAC;CACjC;AAED,kFAAkF;AAClF,eAAO,MAAM,2BAA2B,EAAE,kBAGzC,CAAC;AAmOF;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,EACtB,MAAM,GAAE,kBAAgD,GACvD,kBAAkB,CA6CpB;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAEnE"}
|