Conversation
Three strings in the public spec matched the hook's patterns added in ca94d5b. The hook checks full file contents of staged files, so these pre-existing strings blocked any commit touching the files: - conformance/grammar/kp-pack.schema.json:149 — field description in provenance schema. - spec/SPEC.md:252 — YAML comment in the manifest example. - spec/SPEC.md:777 — evidence example prose. First two were false positives from the role-word regex (matched any letter after the role word, triggering on generic prose). Slight rewording avoids the false positive without changing semantics. Third used phrasing reserved for internal process notes. Reworded to generic aggregation language appropriate for a public example. Also tighten the hook's role-word regex from [A-Z0-9] (over-broad) to [0-9]+\b (numeric attribution only, which is the intended target). Same change applied to pre-commit and commit-msg hooks. Co-Authored-By: Claude <noreply@anthropic.com>
Adds an optional `extensions` object at the PACK.yaml manifest root as the sanctioned lane for experimental or implementation-specific metadata. The manifest root stays closed (additionalProperties: false); new fields belong under `extensions`, not at the top level. Consumers MUST ignore unknown extension content. Extensions MUST NOT redefine core KP semantics. Extension names and shapes are defined by their producers, not by KP:1 — the public spec shows `ai_brief` as one illustrative example, not as a standardized payload. Updated: - CHANGELOG.md — v0.7.4 entry (Added / Changed) - CORE.md — Manifest Extensions prose + field table row - SPEC.md — §3.2 Manifest Extensions + example usage in the YAML demo - kp-pack.schema.json — `extensions` as allowed top-level object, still closed at the root via additionalProperties: false Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a sanctioned “manifest extensions lane” to KP:1 by introducing an optional extensions object at the PACK.yaml root, preserving a closed manifest root (additionalProperties: false) while enabling experimental/implementation-specific metadata.
Changes:
- Adds
extensionsguidance + examples to the spec docs (CORE.md, SPEC.md) and records the change in the changelog (v0.7.4). - Extends the normative PACK.yaml JSON Schema to allow an
extensionsobject while keeping the root closed. - Tightens lefthook deny-list regexes (stacked from #8).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/SPEC.md | Documents the extensions lane and adds an illustrative manifest example + new §3.2 section. |
| spec/CORE.md | Adds extensions to the Core Optional Fields table and provides normative guidance + examples. |
| spec/CHANGELOG.md | Adds v0.7.4 entry describing the manifest extensions lane. |
| lefthook.yml | Updates deny-list grep patterns (process-language detection). |
| conformance/grammar/kp-pack.schema.json | Adds extensions as an allowed top-level object; keeps root additionalProperties: false. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| # Process language (the patterns that actually leaked) | ||
| if grep -inE 'cross-model (review|consultation|spec review)|multi-model (synthesis|analysis|consultation|spec review)|three-model consultation|two-model consultation|round [0-9]+ (review|verify|cross-model|findings)|consulted (codex|gemini|claude)|adversarial review|(codex|gemini|claude) feedback|reviewer [A-Z0-9]|session handover|model attributions' "$file" 2>/dev/null; then | ||
| if grep -inE 'cross-model (review|consultation|spec review)|multi-model (synthesis|analysis|consultation|spec review)|three-model consultation|two-model consultation|round [0-9]+ (review|verify|cross-model|findings)|consulted (codex|gemini|claude)|adversarial review|(codex|gemini|claude) feedback|reviewer [0-9]+\b|session handover|model attributions' "$file" 2>/dev/null; then |
There was a problem hiding this comment.
The pattern reviewer [0-9]+\b is using \b as a word-boundary, but grep -E does not portably support \b as a word-boundary (often treated as backspace). This can make the deny-list miss matches like reviewer 12 on some environments. Use a POSIX-compatible boundary (e.g., reviewer [0-9]+([^0-9]|$)) or switch to grep -P if you want \b semantics.
| if grep -inE 'cross-model (review|consultation|spec review)|multi-model (synthesis|analysis|consultation|spec review)|three-model consultation|two-model consultation|round [0-9]+ (review|verify|cross-model|findings)|consulted (codex|gemini|claude)|adversarial review|(codex|gemini|claude) feedback|reviewer [0-9]+\b|session handover|model attributions' "$file" 2>/dev/null; then | |
| if grep -inE 'cross-model (review|consultation|spec review)|multi-model (synthesis|analysis|consultation|spec review)|three-model consultation|two-model consultation|round [0-9]+ (review|verify|cross-model|findings)|consulted (codex|gemini|claude)|adversarial review|(codex|gemini|claude) feedback|reviewer [0-9]+([^0-9]|$)|session handover|model attributions' "$file" 2>/dev/null; then |
| clean=$(echo "$msg" | sed -E '/^Co-Authored-By: (Claude|Codex|Gemini|Antigravity|Cursor) <noreply@[^>]+>$/d') | ||
|
|
||
| if echo "$clean" | grep -iE 'cross-model|multi-model (synthesis|analysis|consultation|review)|round [0-9]+ (review|verify|findings)|consulted (codex|gemini|claude)|(codex|gemini|claude) (feedback|suggested|reviewed)|adversarial review|reviewer [A-Z0-9]|session handover|model attributions' >/dev/null 2>&1; then | ||
| if echo "$clean" | grep -iE 'cross-model|multi-model (synthesis|analysis|consultation|review)|round [0-9]+ (review|verify|findings)|consulted (codex|gemini|claude)|(codex|gemini|claude) (feedback|suggested|reviewed)|adversarial review|reviewer [0-9]+\b|session handover|model attributions' >/dev/null 2>&1; then |
There was a problem hiding this comment.
Same issue as pre-commit: grep -iE with reviewer [0-9]+\b is not portable because \b isn't reliably a word-boundary in ERE. This can weaken the commit-msg deny-list (false negatives). Prefer a POSIX-safe boundary like reviewer [0-9]+([^0-9]|$) or use grep -P if PCRE is acceptable.
| if echo "$clean" | grep -iE 'cross-model|multi-model (synthesis|analysis|consultation|review)|round [0-9]+ (review|verify|findings)|consulted (codex|gemini|claude)|(codex|gemini|claude) (feedback|suggested|reviewed)|adversarial review|reviewer [0-9]+\b|session handover|model attributions' >/dev/null 2>&1; then | |
| if echo "$clean" | grep -iE 'cross-model|multi-model (synthesis|analysis|consultation|review)|round [0-9]+ (review|verify|findings)|consulted (codex|gemini|claude)|(codex|gemini|claude) (feedback|suggested|reviewed)|adversarial review|reviewer [0-9]+([^0-9]|$)|session handover|model attributions' >/dev/null 2>&1; then |
| "extensions": { | ||
| "type": "object", | ||
| "additionalProperties": true, | ||
| "description": "Extension lane for experimental or implementation-specific manifest metadata. Consumers MUST ignore unrecognized extension content." | ||
| } |
There was a problem hiding this comment.
extensions is now part of the normative schema, but none of the conformance fixtures currently exercise it (including valid/maximal.kpack, which is documented as covering every optional PACK.yaml field). Add an extensions object to the maximal fixture (and optionally an invalid fixture where extensions is non-object) to keep schema coverage claims accurate and prevent regressions.
Adds two layers of defense against real-engagement names landing in this public repository's prose, YAML samples, or pack identifiers (continuation of the 91d5e27 / c6c6167 / f4365c6 de-leak series). Layer 1 — agent awareness (primary): AGENTS.md gains a 9th MUST NOT rule covering real client / collection / vendor / family names. Uses only fictional placeholders in the rule text so the file itself never names a real engagement. Layer 2 — mechanical backstop: lefthook pre-commit and commit-msg hooks gain a `client-names` step that scans staged content and commit messages against patterns loaded from $DENY_PATTERNS_CLIENT_NAMES. The patterns themselves live in a gitignored .private-deny-list file sourced by .lefthookrc (already loaded for PATH setup). A committed .private-deny-list.example documents the shape. Fresh clones without a local patterns file get only the behavioral defense in AGENTS.md — acceptable trade-off, since putting the actual names in the public hook config would defeat the scrub. Files: - .gitignore ignore .private-deny-list - .lefthookrc source .private-deny-list if present - .private-deny-list.example shape doc with placeholder names - lefthook.yml add client-names step to pre-commit + commit-msg - AGENTS.md add MUST NOT #9 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Adds an optional
extensionsobject at the PACK.yaml manifest root as the sanctioned compatibility lane for experimental or implementation-specific metadata. The manifest root stays closed (additionalProperties: false); experimental fields belong underextensions, not at the top level.Why
The spec currently has no sanctioned way to ship experimental manifest metadata without either:
additionalProperties: falseat the root (rejected — the closed root is load-bearing), orThis PR introduces a narrow third path. Consumers MUST ignore unknown extension content. Extensions MUST NOT redefine core semantics. Tools can ship experiments without forcing a schema break or prematurely promoting a payload into the core standard.
Changed
spec/CHANGELOG.md— v0.7.4 entry (Added / Changed)spec/CORE.md— Manifest Extensions prose + field table row + illustrative framingspec/SPEC.md— new §3.2 Manifest Extensions section + YAML exampleconformance/grammar/kp-pack.schema.json—extensionsproperty; root remains closedIllustrative example
The spec uses one payload to make the lane concrete:
The spec explicitly frames this as illustrative only — extension names and shapes are defined by their producers, not by KP:1.
Judgment call —
ai_briefas the public exampleWhether to keep
ai_brief(a real internal concept already consumed by the viewer) or invent a generic placeholder was consulted across two independent models. Split opinion:Decision: keep
ai_brief, but strengthen the "illustrative only, producer-defined" framing in both CORE.md prose and SPEC.md example comment. This threads the dissent without losing the motivating concreteness.Section numbering
Initial pass placed Manifest Extensions at §3.1, which pushed Channels to §3.2 and invalidated two existing
§3.1cross-references in SPEC.md (lines 227 and 1329). Corrected by placing Manifest Extensions at §3.2 instead, so Channels keeps §3.1 and cross-references stay valid.Downstream impact — verified
PackLoader.swift:142–193, 172–177, 211–213) — already parsesextensions.ai_briefand ignores unknown top-level fields. v0.7.4 describes what the viewer already consumes. No viewer code change required.serialize.ts:1181,types.ts:443,assemble.ts:88) — already emitsextensions.ai_brief. No sealer change required.extensionssurface. Transparent.repos/packs— all 54 manifests validate against the updated schema. Zero migration.In other words: v0.7.4 is the spec catching up to what the tooling already produces. No coordinated downstream rollout is required.
Verified locally
lefthook run pre-commit— all rules passpython3 conformance/run.py— 12/12 passmarkdownlint '**/*.md'— clean