← back to Cli Printing Press
fix(cli): exempt vendor OpenAPI/Swagger source under .manuscripts from PII scan (#1496)
dc0651ab8bf2cb1f9060b14a31de836df7f91ef3 · 2026-05-16 00:15:27 -0700 · Trevin Chow
* fix(cli): exempt vendor-published OpenAPI/Swagger source under .manuscripts from PII scan
The PII gate already exempts root-level spec.{json,yaml,yml} on the
premise that vendor `example:` blocks are documentation, not customer
PII. The exemption was depth-1 only, so a generation that archived the
vendor's published per-resource spec files (`apps/calendars.json`,
`pushpress-v3.yaml`, etc.) under .manuscripts/.../research/ false-failed
publish on the documentation-shape values inside.
Adds a content-detected variant of the same exemption: when a file
anywhere inside a .manuscripts/ subtree begins with an OpenAPI 2.x/3.x
or Swagger 2.0 root-document marker, scanPIIFile skips it. Content
detection beats filename heuristics because vendors ship spec source
under arbitrary basenames that no glob would reliably cover.
The exemption is scoped to .manuscripts/ so non-archive paths
(committed docs/, generated internal/, testdata/ fixtures) still scan.
HARs, session-state captures, and hand-edited proofs under
.manuscripts/ also keep scanning because they carry no version marker.
A complementary regression test pins both directions.
Closes #1356
* fix(cli): anchor JSON vendor-spec markers to document root
Greptile flagged that the JSON markers (`"openapi"\s*:\s*"[23]\.`) had
no document-root anchor, so a non-spec JSON file under .manuscripts/
with a nested `"openapi": "3.0.0"` field deep in its payload (response
envelope, captured config blob, metadata wrapper) would silently bypass
PII scanning. The YAML markers were already anchored via `(?m)^` to
column 0, which in valid YAML means a root-level key.
Tighten the JSON regexes to `\A\s*\{\s*"openapi"\s*:\s*"[23]\.`,
requiring `openapi` (or `swagger`) as the first key after the opening
brace. JSON specs put the version key at root by convention; the strict
anchor matches the YAML behavior and forecloses the nested-key bypass.
Adds three regex-level cases (nested openapi inside `user_data`, info
before openapi, nested swagger) and an end-to-end FindPII regression
test that builds a captured.json with a nested openapi field plus real
PII and asserts the file still scans.
* fix(cli): anchor YAML vendor-spec markers to document start
Greptile's second pass flagged the symmetric YAML gap. The previous
`(?m)^openapi\s*:` regex fired on any column-0 occurrence within the
8 KB head probe — so a research-notes YAML under .manuscripts/ that
listed real PII in earlier root-level keys and a later `openapi: 3.0.0`
key would silently bypass scanning. The JSON anchor (`\A\s*\{\s*"openapi"`)
forecloses the same shape; YAML needed parallel treatment.
Anchor both YAML markers to document start with an explicit allowed
lead-in (optional YAML directives `%...`, document marker `---`,
comment lines, blank lines). Any other content before `openapi`/`swagger`
at column 0 rejects the exemption.
Adds:
- four regex-level cases: yaml-pii-before-openapi (negative), yaml-with-
document-marker (positive), yaml-with-comment-prefix (positive),
yaml-with-directive-and-marker (positive), yaml-with-blank-lead
(positive), yaml-swagger-after-meta (negative).
- a YAMLPIIBeforeOpenAPI FindPII regression that mirrors the JSON one.
---------
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Files touched
M internal/artifacts/pii.goM internal/artifacts/pii_test.goM skills/printing-press-polish/references/pii-polish.md
Diff
commit dc0651ab8bf2cb1f9060b14a31de836df7f91ef3
Author: Trevin Chow <trevin@trevinchow.com>
Date: Sat May 16 00:15:27 2026 -0700
fix(cli): exempt vendor OpenAPI/Swagger source under .manuscripts from PII scan (#1496)
* fix(cli): exempt vendor-published OpenAPI/Swagger source under .manuscripts from PII scan
The PII gate already exempts root-level spec.{json,yaml,yml} on the
premise that vendor `example:` blocks are documentation, not customer
PII. The exemption was depth-1 only, so a generation that archived the
vendor's published per-resource spec files (`apps/calendars.json`,
`pushpress-v3.yaml`, etc.) under .manuscripts/.../research/ false-failed
publish on the documentation-shape values inside.
Adds a content-detected variant of the same exemption: when a file
anywhere inside a .manuscripts/ subtree begins with an OpenAPI 2.x/3.x
or Swagger 2.0 root-document marker, scanPIIFile skips it. Content
detection beats filename heuristics because vendors ship spec source
under arbitrary basenames that no glob would reliably cover.
The exemption is scoped to .manuscripts/ so non-archive paths
(committed docs/, generated internal/, testdata/ fixtures) still scan.
HARs, session-state captures, and hand-edited proofs under
.manuscripts/ also keep scanning because they carry no version marker.
A complementary regression test pins both directions.
Closes #1356
* fix(cli): anchor JSON vendor-spec markers to document root
Greptile flagged that the JSON markers (`"openapi"\s*:\s*"[23]\.`) had
no document-root anchor, so a non-spec JSON file under .manuscripts/
with a nested `"openapi": "3.0.0"` field deep in its payload (response
envelope, captured config blob, metadata wrapper) would silently bypass
PII scanning. The YAML markers were already anchored via `(?m)^` to
column 0, which in valid YAML means a root-level key.
Tighten the JSON regexes to `\A\s*\{\s*"openapi"\s*:\s*"[23]\.`,
requiring `openapi` (or `swagger`) as the first key after the opening
brace. JSON specs put the version key at root by convention; the strict
anchor matches the YAML behavior and forecloses the nested-key bypass.
Adds three regex-level cases (nested openapi inside `user_data`, info
before openapi, nested swagger) and an end-to-end FindPII regression
test that builds a captured.json with a nested openapi field plus real
PII and asserts the file still scans.
* fix(cli): anchor YAML vendor-spec markers to document start
Greptile's second pass flagged the symmetric YAML gap. The previous
`(?m)^openapi\s*:` regex fired on any column-0 occurrence within the
8 KB head probe — so a research-notes YAML under .manuscripts/ that
listed real PII in earlier root-level keys and a later `openapi: 3.0.0`
key would silently bypass scanning. The JSON anchor (`\A\s*\{\s*"openapi"`)
forecloses the same shape; YAML needed parallel treatment.
Anchor both YAML markers to document start with an explicit allowed
lead-in (optional YAML directives `%...`, document marker `---`,
comment lines, blank lines). Any other content before `openapi`/`swagger`
at column 0 rejects the exemption.
Adds:
- four regex-level cases: yaml-pii-before-openapi (negative), yaml-with-
document-marker (positive), yaml-with-comment-prefix (positive),
yaml-with-directive-and-marker (positive), yaml-with-blank-lead
(positive), yaml-swagger-after-meta (negative).
- a YAMLPIIBeforeOpenAPI FindPII regression that mirrors the JSON one.
---------
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
---
internal/artifacts/pii.go | 70 ++++++-
internal/artifacts/pii_test.go | 229 +++++++++++++++++++++
.../printing-press-polish/references/pii-polish.md | 4 +-
3 files changed, 300 insertions(+), 3 deletions(-)
diff --git a/internal/artifacts/pii.go b/internal/artifacts/pii.go
index 57acfc53..b3aa3478 100644
--- a/internal/artifacts/pii.go
+++ b/internal/artifacts/pii.go
@@ -173,14 +173,71 @@ var excludedFiles = map[string]bool{
// addresses) are documentation, not customer PII, so a Stripe/Zendesk/
// GitHub spec doesn't false-fail every promote. Exemption is depth-1
// only; a spec.yaml nested under .manuscripts/ or testdata/ is captured
-// content and stays in scope. Mirrors findArchivedSpec()'s candidate
-// set in internal/pipeline/climanifest.go.
+// content and stays in scope unless content-detection identifies it as
+// an archived vendor spec (see manuscriptsDir + looksLikeVendorAPISpec).
+// Mirrors findArchivedSpec()'s candidate set in
+// internal/pipeline/climanifest.go.
var rootVendorSpecFiles = map[string]bool{
"spec.json": true,
"spec.yaml": true,
"spec.yml": true,
}
+// manuscriptsDir is the path component that scopes the content-detected
+// vendor-spec exemption. Vendor specs archived under any depth inside
+// .manuscripts/ — research/, discovery/, or freeform subdirs — are
+// reproducible from the upstream URL and contain documentation `example:`
+// values, not customer PII. Limiting the content exemption to manuscripts
+// keeps the bypass off non-archive paths (committed docs/, generated
+// internal/, etc.) so a hand-edited file at docs/api.yaml still scans.
+const manuscriptsDir = ".manuscripts"
+
+// vendorSpecMarkers are the OpenAPI/Swagger version-marker patterns
+// looksLikeVendorAPISpec probes for in a file's head bytes. The two-form
+// shape (JSON quoted-key vs YAML unquoted-key) is required because vendor
+// docs ship both formats. Both forms anchor the key to the document
+// start: JSON requires `openapi`/`swagger` as the first key after the
+// opening brace; YAML requires it as the first content line after any
+// allowed lead-in (YAML directive `%...`, document marker `---`,
+// comments, blank lines). Without these anchors, a non-spec file with
+// PII in earlier keys could bypass scanning when it happened to also
+// contain an `openapi`/`swagger` marker deeper in the payload — a
+// response envelope, captured config blob, or research-notes YAML that
+// listed real values before the version field. Version constraints
+// (2.x or 3.x) avoid matching freeform mentions like "openapi: future".
+var vendorSpecMarkers = []*regexp.Regexp{
+ // JSON: {"openapi": "3.x.x" — openapi must be the first key.
+ regexp.MustCompile(`\A\s*\{\s*"openapi"\s*:\s*"[23]\.`),
+ regexp.MustCompile(`\A\s*\{\s*"swagger"\s*:\s*"2\.`),
+ // YAML: optional %directives, `---` document marker, comments, and
+ // blank lines may precede the marker, but no other content key can
+ // appear before `openapi`/`swagger` at column 0.
+ regexp.MustCompile(`\A(?:(?:%[^\n]*|---[ \t]*|[ \t]*#[^\n]*|[ \t]*)\n)*openapi\s*:\s*['"]?[23]\.`),
+ regexp.MustCompile(`\A(?:(?:%[^\n]*|---[ \t]*|[ \t]*#[^\n]*|[ \t]*)\n)*swagger\s*:\s*['"]?2\.`),
+}
+
+// looksLikeVendorAPISpec reports whether the first few KB of a file
+// match an OpenAPI 2.x/3.x or Swagger 2.0 root-document marker. Content
+// detection beats filename heuristics because vendors ship spec source
+// under arbitrary basenames (`apps/calendars.json`, `pushpress-v3.yaml`)
+// that no glob would reliably cover. Restricted at the call site to
+// files under .manuscripts/ so non-archive paths cannot bypass scanning
+// by embedding a stray version marker.
+func looksLikeVendorAPISpec(probe []byte) bool {
+ for _, re := range vendorSpecMarkers {
+ if re.Match(probe) {
+ return true
+ }
+ }
+ return false
+}
+
+// isUnderManuscripts reports whether the slash-separated relative path
+// lives anywhere inside a .manuscripts/ subtree.
+func isUnderManuscripts(relSlash string) bool {
+ return slices.Contains(strings.Split(relSlash, "/"), manuscriptsDir)
+}
+
// skippedDirs are subtree names the walker never descends into at the
// top level. Scoping to depth-1 is deliberate — `.git` and friends as
// direct children of the cli-dir are infrastructure; the same names
@@ -307,6 +364,15 @@ func scanPIIFile(root, path string) ([]PIIFinding, error) {
}
relSlash := filepath.ToSlash(rel)
+ // Vendor-published OpenAPI/Swagger source archived under .manuscripts/
+ // carries documentation `example:` values, not customer PII. Mirrors
+ // the depth-1 rootVendorSpecFiles exemption for the case where the
+ // spec source lives nested in research/, discovery/, or freeform
+ // archive subdirs and the basename isn't `spec.{json,yaml,yml}`.
+ if isUnderManuscripts(relSlash) && looksLikeVendorAPISpec(probe) {
+ return nil, nil
+ }
+
var findings []PIIFinding
lineNumber := 0
for {
diff --git a/internal/artifacts/pii_test.go b/internal/artifacts/pii_test.go
index d6a3f82a..17de3d41 100644
--- a/internal/artifacts/pii_test.go
+++ b/internal/artifacts/pii_test.go
@@ -233,6 +233,10 @@ func TestFindPII_RootVendorSpecExempt(t *testing.T) {
// - arbitrary subdirs (output/) match via the *.yaml entry in
// highRiskFileGlobs; pinned here as a regression guard against a
// future tweak that broadens the exemption from depth-1 to all paths
+//
+// The companion exemption added in #1356 only triggers when the file
+// looks like an OpenAPI/Swagger root document. Files without that
+// marker still scan even when nested under .manuscripts/.
func TestFindPII_NestedSpecYamlStillScans(t *testing.T) {
root := t.TempDir()
pii := `"email": "captured@victim.com"`
@@ -252,6 +256,231 @@ func TestFindPII_NestedSpecYamlStillScans(t *testing.T) {
assert.Contains(t, files, "output/spec.yaml")
}
+// #1356: Vendor-published OpenAPI/Swagger specs archived under
+// .manuscripts/ are documentation source — `example:` values look like
+// PII but are public. The content-based exemption triggers off the
+// version-marker shape so basenames like apps/calendars.json or
+// pushpress-v3.yaml (which no filename glob would reliably cover) are
+// also exempted.
+func TestFindPII_ManuscriptsVendorSpecExempt(t *testing.T) {
+ root := t.TempDir()
+ require.NoError(t, os.MkdirAll(filepath.Join(root, ".manuscripts", "run1", "research", "apps"), 0755))
+ require.NoError(t, os.MkdirAll(filepath.Join(root, ".manuscripts", "run1", "research"), 0755))
+
+ // OpenAPI 3.x JSON with documentation-shape PII.
+ openapiJSON := `{
+ "openapi": "3.0.3",
+ "info": {"title": "Calendars"},
+ "paths": {
+ "/users": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "example": {"email": "user1@testemail.com", "phone": "(415) 555-0123"}
+ }
+ }
+ }
+ }
+ }
+ }
+}`
+ write(t, filepath.Join(root, ".manuscripts", "run1", "research", "apps", "calendars.json"), openapiJSON)
+
+ // OpenAPI 3.x YAML with documentation-shape PII.
+ openapiYAML := `openapi: 3.0.0
+info:
+ title: PushPress v3
+paths:
+ /bookings:
+ post:
+ requestBody:
+ content:
+ application/json:
+ example:
+ email: info@acme.com
+ phone: 1-800-555-1234
+ address: 123 Main Street
+`
+ write(t, filepath.Join(root, ".manuscripts", "run1", "research", "pushpress-v3.yaml"), openapiYAML)
+
+ // Swagger 2.0 JSON with documentation-shape PII.
+ swaggerJSON := `{
+ "swagger": "2.0",
+ "info": {"title": "Legacy"},
+ "paths": {"/u": {"get": {"responses": {"200": {"examples": {"application/json": {"email": "x@y.com"}}}}}}}
+}`
+ write(t, filepath.Join(root, ".manuscripts", "run1", "research", "legacy.json"), swaggerJSON)
+
+ findings, err := FindPII(root)
+ require.NoError(t, err)
+
+ files := uniqueFiles(findings)
+ assert.NotContains(t, files, ".manuscripts/run1/research/apps/calendars.json",
+ "OpenAPI 3.x JSON in manuscripts must be exempt")
+ assert.NotContains(t, files, ".manuscripts/run1/research/pushpress-v3.yaml",
+ "OpenAPI 3.x YAML in manuscripts must be exempt")
+ assert.NotContains(t, files, ".manuscripts/run1/research/legacy.json",
+ "Swagger 2.0 JSON in manuscripts must be exempt")
+}
+
+// Negative regression: vendor-spec content detection only applies inside
+// .manuscripts/. A file at docs/api.yaml or testdata/openapi.json with
+// OpenAPI markers still scans — those are committed, hand-curated
+// artifacts that could legitimately accumulate real PII.
+func TestFindPII_VendorSpecOutsideManuscriptsStillScans(t *testing.T) {
+ root := t.TempDir()
+ openapiYAML := `openapi: 3.0.0
+info:
+ title: Public API
+paths:
+ /u:
+ post:
+ requestBody:
+ content:
+ application/json:
+ example:
+ email: leaked@victim.com
+`
+ require.NoError(t, os.MkdirAll(filepath.Join(root, "docs"), 0755))
+ require.NoError(t, os.MkdirAll(filepath.Join(root, "testdata"), 0755))
+ write(t, filepath.Join(root, "docs", "api.yaml"), openapiYAML)
+ write(t, filepath.Join(root, "testdata", "openapi.json"), `{"openapi":"3.0.0","info":{"title":"x"},"paths":{"/u":{"post":{"requestBody":{"content":{"application/json":{"example":{"email":"leaked@victim.com"}}}}}}}}`)
+
+ findings, err := FindPII(root)
+ require.NoError(t, err)
+
+ files := uniqueFiles(findings)
+ assert.Contains(t, files, "docs/api.yaml",
+ "vendor-spec exemption must not bypass docs/ committed artifacts")
+ assert.Contains(t, files, "testdata/openapi.json",
+ "vendor-spec exemption must not bypass testdata fixtures")
+}
+
+// Negative regression: HARs and session-state captures under
+// .manuscripts/ keep scanning. They have no OpenAPI marker, so
+// looksLikeVendorAPISpec returns false and the exemption never fires.
+func TestFindPII_NonSpecManuscriptCapturesStillScan(t *testing.T) {
+ root := t.TempDir()
+ require.NoError(t, os.MkdirAll(filepath.Join(root, ".manuscripts", "run1", "discovery"), 0755))
+
+ har := `{
+ "log": {
+ "version": "1.2",
+ "entries": [
+ {"request": {"url": "https://api.example.com"}, "response": {"content": {"text": "{\"email\":\"real@user.com\",\"phone\":\"(415) 555-0123\"}"}}}
+ ]
+ }
+}`
+ write(t, filepath.Join(root, ".manuscripts", "run1", "discovery", "session.har"), har)
+
+ sessionState := `{
+ "cookies": [{"name": "auth_token", "value": "secret"}],
+ "user": {"email": "real@user.com", "address": "1234 Main Street"}
+}`
+ write(t, filepath.Join(root, ".manuscripts", "run1", "discovery", "session-state.json"), sessionState)
+
+ findings, err := FindPII(root)
+ require.NoError(t, err)
+
+ files := uniqueFiles(findings)
+ assert.Contains(t, files, ".manuscripts/run1/discovery/session.har")
+ assert.Contains(t, files, ".manuscripts/run1/discovery/session-state.json")
+}
+
+// The exemption probes a head window for the marker. A file that buries
+// `openapi: 3.0` deep past the probe window does not bypass scanning —
+// the head-window bound is intentional so an attacker (or large
+// hand-edited proof) cannot smuggle PII past the gate by prepending
+// arbitrary unrelated content.
+func TestLooksLikeVendorAPISpec(t *testing.T) {
+ tests := []struct {
+ name string
+ probe string
+ want bool
+ }{
+ {"openapi-3.0-json", `{"openapi": "3.0.3"}`, true},
+ {"openapi-3.1-json", `{"openapi":"3.1.0"}`, true},
+ {"openapi-2.0-json", `{"openapi": "2.0"}`, true},
+ {"swagger-2.0-json", `{"swagger": "2.0"}`, true},
+ {"openapi-3.0-json-pretty", "{\n \"openapi\": \"3.0.3\",\n \"info\": {}\n}", true},
+ {"openapi-yaml", "openapi: 3.0.0\ninfo:\n title: x", true},
+ {"openapi-yaml-quoted", `openapi: "3.0.0"`, true},
+ {"swagger-yaml", "swagger: '2.0'\ninfo:\n title: x", true},
+ {"plain-json", `{"users": [{"email": "x@y.com"}]}`, false},
+ {"plain-yaml", "users:\n - email: x@y.com\n", false},
+ {"prose-mentioning-openapi", "This file describes the openapi: 3.0 schema we built.", false},
+ {"openapi-mid-line-yaml", " openapi: 3.0", false},
+ {"openapi-future-version-string", `{"openapi": "4.0"}`, false},
+ // Anchor regressions: only root-level keys may signal a vendor
+ // spec. A non-spec JSON file whose nested payload contains an
+ // `openapi`/`swagger` field must not trip the exemption.
+ {"json-nested-openapi-field", `{"user_data": {"extras": {"openapi": "3.0.0", "email": "real@user.com"}}}`, false},
+ {"json-info-before-openapi", `{"info": {"title": "x"}, "openapi": "3.0.0"}`, false},
+ {"json-swagger-nested", `{"meta": {"swagger": "2.0"}}`, false},
+ // YAML anchor regressions: PII-shaped values in earlier root
+ // keys must not be exempted just because a later root key looks
+ // like an OpenAPI/Swagger version marker.
+ {"yaml-pii-before-openapi", "captured_at: 2026-05-15\nuser_email: real@user.com\nopenapi: 3.0.0", false},
+ {"yaml-swagger-after-meta", "tenant: acme\nswagger: '2.0'", false},
+ // YAML lead-in allowed: optional document marker, directives,
+ // comment lines, and blank lines before the version marker.
+ {"yaml-with-document-marker", "---\nopenapi: 3.0.0", true},
+ {"yaml-with-comment-prefix", "# Vendor-published OpenAPI spec\nopenapi: 3.0.0", true},
+ {"yaml-with-directive-and-marker", "%YAML 1.2\n---\nopenapi: 3.0.0", true},
+ {"yaml-with-blank-lead", "\nopenapi: 3.0.0", true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equal(t, tt.want, looksLikeVendorAPISpec([]byte(tt.probe)))
+ })
+ }
+}
+
+// Anchor regression at the file walk level: a JSON file under
+// .manuscripts/ whose first 8 KB contains a nested "openapi": "3.0.0"
+// (but no root-level marker) must still scan. Pins the JSON anchor's
+// intent so a future weakening of the regex surfaces here as a real
+// PII gate regression instead of a silent bypass.
+func TestFindPII_NestedOpenAPIFieldStillScans(t *testing.T) {
+ root := t.TempDir()
+ require.NoError(t, os.MkdirAll(filepath.Join(root, ".manuscripts", "run1", "research"), 0755))
+ nestedJSON := `{
+ "captured_at": "2026-05-15",
+ "payload": {
+ "request": {"openapi": "3.0.0"},
+ "response": {"email": "real@user.com", "phone": "(415) 555-0123"}
+ }
+}`
+ write(t, filepath.Join(root, ".manuscripts", "run1", "research", "captured.json"), nestedJSON)
+
+ findings, err := FindPII(root)
+ require.NoError(t, err)
+ assert.Contains(t, uniqueFiles(findings), ".manuscripts/run1/research/captured.json",
+ "nested openapi field must not bypass PII scanning")
+}
+
+// YAML parallel: a research-notes YAML under .manuscripts/ that lists
+// real captured PII in earlier root keys and a later `openapi: 3.0.0`
+// root key must still scan. Without the document-start anchor on the
+// YAML marker, the column-0 `openapi:` line would trip the exemption
+// and silently exempt the PII above it.
+func TestFindPII_YAMLPIIBeforeOpenAPIStillScans(t *testing.T) {
+ root := t.TempDir()
+ require.NoError(t, os.MkdirAll(filepath.Join(root, ".manuscripts", "run1", "research"), 0755))
+ notes := `captured_at: 2026-05-15
+user_email: real@user.com
+phone: (415) 555-0123
+openapi: 3.0.0
+`
+ write(t, filepath.Join(root, ".manuscripts", "run1", "research", "notes.yaml"), notes)
+
+ findings, err := FindPII(root)
+ require.NoError(t, err)
+ assert.Contains(t, uniqueFiles(findings), ".manuscripts/run1/research/notes.yaml",
+ "YAML with PII before a root-level openapi marker must not bypass PII scanning")
+}
+
func TestFindPII_BinaryFileSkip(t *testing.T) {
root := t.TempDir()
// Planted PII in a "json" file with embedded nulls (mimics binary)
diff --git a/skills/printing-press-polish/references/pii-polish.md b/skills/printing-press-polish/references/pii-polish.md
index 66f6a96e..7d77422f 100644
--- a/skills/printing-press-polish/references/pii-polish.md
+++ b/skills/printing-press-polish/references/pii-polish.md
@@ -70,7 +70,9 @@ When a downstream consumer (parser, schema validator) needs a valid-shape value,
**Manuscripts (`.manuscripts/<runID>/`).** Highest-risk source — captured browser-sniff content is by construction where customer values entered. Acceptance for manuscript findings requires the explicit `evidence_context`-cited justification; "captured data" alone is not sufficient. When in doubt, replace the value in the manuscript file directly (it's local working state) and use a hand-authored fixture with synthetic values for any downstream tests.
-**Vendor spec files at the CLI root (`spec.yaml`, `spec.yml`, `spec.json`).** Exempt from the audit by design — these are the OpenAPI/internal source the operator passed to `--spec`, and vendor `example:` blocks (Stripe `jenny@example.com`, GitHub user-schema example phones) are documentation, not customer PII. The exemption is depth-1 only; the same basename nested under `.manuscripts/` or `testdata/` is captured content and stays in scope.
+**Vendor spec files at the CLI root (`spec.yaml`, `spec.yml`, `spec.json`).** Exempt from the audit by design — these are the OpenAPI/internal source the operator passed to `--spec`, and vendor `example:` blocks (Stripe `jenny@example.com`, GitHub user-schema example phones) are documentation, not customer PII. The exemption is depth-1 only; the same basename nested under `testdata/` is captured content and stays in scope.
+
+**Vendor spec source archived under `.manuscripts/`.** Files anywhere inside a `.manuscripts/` subtree whose head bytes match an OpenAPI 2.x/3.x or Swagger 2.0 root-document marker are exempt — the operator archived the vendor's published spec source alongside research, and its `example:` blocks are documentation. The exemption is content-based, not glob-based, so per-resource subdir basenames like `apps/calendars.json` or vendor-named files like `pushpress-v3.yaml` are covered without an allowlist. HARs, `session-state.json`, hand-edited proofs, and any other non-spec content under `.manuscripts/` stays in scope because the marker check fails on them.
## Forbidden accept patterns
← 9d3050b5 fix(cli): always emit boolean body fields, no zero-guard (#1
·
back to Cli Printing Press
·
fix(cli): refresh auth on redirects via CheckRedirect (#1497 f3f70435 →