---
phase: 14-domain-jobs-and-external-integrations
plan: 06
type: execute
wave: 6
depends_on: ["14-05"]
files_modified:
- scripts/check-phase14.sh
- modules/fetchguard/client_coverage_test.go
- modules/tide/upstream_coverage_test.go
- modules/sunscreen/sunscreen_coverage_test.go
- modules/beachcomber/typesense/engine_test.go
- ../fonoteka.go/parity/discogs_truth_tables.php
- ../fonoteka.go/parity/README.md
- ../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/testdata/
- ../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/php_truth_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/coverage_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/phase14_security_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/phase14_edges_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/phase14_jobs_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/write_endpoints_fuzz_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/testdata/fuzz/FuzzWriteEndpoints/
- ../fonoteka.go/plugins/golem15/fonoteka/routes_table_phase14_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/console/phase14_commands_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/phase14_controllers_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/classes/phase14_classes_test.go
- ../fonoteka.go/plugins/golem15/golem/classes/services/coverage_test.go
- ../fonoteka.go/plugins/golem15/golem/classes/providers/coverage_test.go
- ../fonoteka.go/plugins/golem15/golem/golem_admin_test.go
- ../fonoteka.go/plugins/golem15/golem
- ../fonoteka.go/plugins/golem15/feedback/feedback_edges_test.go
- ../fonoteka.go/plugins/golem15/feedback/classes/coverage_test.go
- ../fonoteka.go/plugins/golem15/feedback
- .planning/phases/14-domain-jobs-and-external-integrations/14-SECURITY-REVIEW.md
- .planning/phases/14-domain-jobs-and-external-integrations/14-VALIDATION.md
- .planning/phases/14-domain-jobs-and-external-integrations/COVERAGE.md
- .planning/REQUIREMENTS.md
- .planning/todos/pending/fetchguard-guarded-http-client.md
- .planning/todos/pending/redacting-slog-handler.md
- .planning/todos/done/fetchguard-guarded-http-client.md
- .planning/todos/done/redacting-slog-handler.md
autonomous: true
requirements: [JOBS-02, JOBS-03, SRCH-02, INTG-01, INTG-02, API-08, CLI-05]
estimate:
tokens: 380000
raw_tokens: 380000
tasks: 4
confidence: low
must_haves:
truths:
- "`scripts/check-phase14.sh` (summercms.go) is fail-closed with `--self-test`, `--go`, `--parity`, `--named`, `--removal`, `--coverage`, `--evidence` and `--all`; `--all` (every stage except `--removal`) runs vet and tests in summercms.go and in fonoteka.go including both new submodule plugins with -race, the parity corpus at exactly 175 recorded, 172 ported and passing, 3 pending (the D-09 routes), every TestFonotekaNuxtFlows subtest, TestUpstreamSidecarsAreReplayed, check_corpus `--require-recorded --check-secrets`, TestDocsTree and docs:build --check; `--self-test` proves each detector fails on planted input."
- "PHP truth tables generated by `parity/discogs_truth_tables.php` (running the PHP DiscogsMapper, DiscogsInputParser, ReleaseMatchScorer and PriceSuggestionResolver directly, as csv_truth_tables.php does) are committed under classes/discogs/testdata/php_*.json and `TestPHPTruthDiscogs` reproduces every row byte for byte, including price rounding."
- "Every Go package created or changed in Phase 14 reaches at least 80% statement coverage: summercms.go fetchguard, tide, sunscreen, beachcomber and beachcomber/typesense; fonoteka classes/discogs and console; every Phase 14 function in the fonoteka root, classes and controllers/api packages at 80% by `go tool cover -func`; every package of sm-golem-plugin and sm-feedback-plugin; the numbers are recorded in 14-VALIDATION.md."
- "`TestRouteTablePhase14` pins all eleven Phase 14 fonoteka routes and the four feedback routes (group, method, constraints, scope, throttles and buckets), and `FuzzWriteEndpoints` covers the Phase 14 write routes (apply-release, import/discogs, recognize, cover-price, credential tests, feedback submit and me/hidden) asserting no column outside each allow-list changes and no 500 PHP does not answer."
- "Each mitigated T-14 threat of plans 14-01 to 14-05 has a named test in `TestPhase14Threats` (or the named module test) that fails when its protection is removed; `check-phase14.sh --removal` applies anchor-exact mutations, requires the named test to fail on an assertion and restores each file byte for byte (cmp); 14-SECURITY-REVIEW.md maps every T-14 id to category, severity, disposition, protecting file and that test."
- "Every edge truth of plans 14-02 to 14-05 is pinned one step either side in `TestPhase14Edges` and the feedback edge tests (limiter threshold and budget, Retry-After parsing, MAX_CANDIDATES, prune cutoff, inbound limits 60/20/10/12, feedback validation limits), and each prohibition of plans 14-01 to 14-05 has a negative test the gate requires by name."
- "Per D-06, D-07, D-13 and D-14, `--evidence` refuses a REQUIREMENTS.md that still carries the SDK wording for INTG-02 or sitemap for API-08, and a ROADMAP Phase 14 section without the album Discogs and recognize routes or the sm-golem-plugin and sm-feedback-plugin repos; REQUIREMENTS.md marks JOBS-02, JOBS-03, SRCH-02, INTG-01, INTG-02, API-08 and CLI-05 Complete; 14-VALIDATION.md has real task ids, no pending row, `nyquist_compliant: true` and `wave_0_complete: true`; the two folded todos move to `.planning/todos/done/`."
- "COVERAGE.md lists every Discogs, Anthropic, OpenAI-compatible and G15Office capability the PHP reference uses as INTEGRATE with a named test, and every other capability as OPT-OUT with a reason; `--evidence` refuses an INTEGRATE row without a passing named test."
- "PriceSuggestionResolver results equal PHP's for every truth-table input (every currency, empty suggestions, rounding ties), pinned by TestPHPTruthDiscogs."
artifacts:
- path: "scripts/check-phase14.sh"
provides: "fail-closed Phase 14 gate"
contains: "EXPECTED_PORTED=172"
- path: "../fonoteka.go/parity/discogs_truth_tables.php"
provides: "PHP truth-table generator for the Discogs pure classes"
contains: "PriceSuggestionResolver"
- path: "../fonoteka.go/plugins/golem15/fonoteka/phase14_security_test.go"
provides: "TestPhase14Threats"
contains: "TestPhase14Threats"
- path: ".planning/phases/14-domain-jobs-and-external-integrations/14-SECURITY-REVIEW.md"
provides: "threat-to-test evidence"
key_links:
- from: "scripts/check-phase14.sh"
to: ".planning/phases/14-domain-jobs-and-external-integrations/14-VALIDATION.md"
via: "--named reads every test the validation map names; --evidence refuses pending or TBD rows"
pattern: "14-VALIDATION.md"
- from: "../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/php_truth_test.go"
to: "../fonoteka.go/parity/discogs_truth_tables.php"
via: "testdata/php_*.json written by the PHP generator"
pattern: "php_"
prohibitions:
- requirement_id: INTG-01
category: transparency
statement: "Pending routes MUST NOT be counted as passing; the three D-09 routes stay pending and the gate fails if any pending count differs from 3"
status: resolved
verification: test
- requirement_id: INTG-02
category: transparency
statement: "A threat MUST NOT be marked mitigated in 14-SECURITY-REVIEW.md without a named test that was run and seen to fail when its protection is removed"
status: resolved
verification: test
---
## Phase Goal
ROADMAP Phase 14 goal (verbatim, not in user-story form): The domain-specific River jobs (CSV import write, Discogs match, wishlist digest), the reindex command, the Discogs client and AI cover recognition are ported on top of the Phase 11 jobs/realtime/search infrastructure and the Phase 13 API surface they serve.
This plan's slice: the phase is proven. Every Phase 14 package has full unit coverage, the PHP truth tables pin the Discogs rules, each threat has a test that fails without its protection, and one gate script says green or red for the whole phase (lean-mode rule 3: unit tests are the last plan).
Write the PHP truth-table generator and tests, bring every Phase 14 package to at least 80% coverage with edge and threat tests, extend the route-table and fuzz tests, build `scripts/check-phase14.sh`, and record the security review, validation evidence, requirement status and API coverage.
Purpose: CLAUDE.md lean-mode rule 3; the gate is what `/gsd-verify-work 14` runs. Decisions verified: every D-01 to D-21 that produced code, plus the D-06/D-07/D-13/D-14 rewording.
Output: tests in all four repositories, the gate script, 14-SECURITY-REVIEW.md, a validated 14-VALIDATION.md, COVERAGE.md checks, REQUIREMENTS statuses.
Repos: summercms.go (gate, framework tests, planning docs in a separate commit), fonoteka.go, sm-golem-plugin and sm-feedback-plugin (tests committed and pushed in each checkout, then one pointer-bump commit per plugin in fonoteka.go). Never add co-author tags.
@~/.claude/gsd-core/workflows/execute-plan.md
@~/.claude/gsd-core/templates/summary.md
@.planning/phases/14-domain-jobs-and-external-integrations/14-VALIDATION.md
@.planning/phases/14-domain-jobs-and-external-integrations/COVERAGE.md
@.planning/phases/14-domain-jobs-and-external-integrations/14-01-SUMMARY.md
@.planning/phases/14-domain-jobs-and-external-integrations/14-02-SUMMARY.md
@.planning/phases/14-domain-jobs-and-external-integrations/14-03-SUMMARY.md
@.planning/phases/14-domain-jobs-and-external-integrations/14-04-SUMMARY.md
@.planning/phases/14-domain-jobs-and-external-integrations/14-05-SUMMARY.md
@scripts/check-phase13.sh
- check-phase13.sh structure: env overrides (`PHASE13_ROOT`, `PHASE13_APP`, `PHASE13_PHASE_DIR`), `APP_PLUGINS`, `EXPECTED_PORTED`, `EXPECTED_PENDING`, `COVERAGE_FLOOR=80`, the python `go test -json` detector (exit 1 fail, 2 skip, 3 zero tests, 4 non-JSON, 5 required test missing, 6 race), `expect_detect`, `run_go`, `corpus_scan`, `manifest_count`, `run_parity`, `run_named`, `coverage_report`, `func_floor`, `removal_table`, `removal_harness` (refuses dirty files, cmp restore), `evidence_check`, `run_self_test`.
- 13-06 precedents: `FuzzWriteEndpoints` with its committed seed corpus under testdata/fuzz, `TestPhase13Threats`, route-table tests over `surf.BuildRouter(...).Routes()`.
- parity/csv_truth_tables.php (PHP_ROOT env, require_once the classes, stub ApplicationException, write_table with JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION).
- Threat ids T-14-01 to T-14-36 and T-14-SC from the five earlier plans' threat models.
## Artifacts this phase produces
(This plan's share.)
- `scripts/check-phase14.sh` (`--self-test --go --parity --named --removal --coverage --evidence --all`; `PHASE14_ROOT`, `PHASE14_APP`, `PHASE14_PHASE_DIR`; `EXPECTED_ROUTES=175`, `EXPECTED_PORTED=172`, `EXPECTED_PENDING=3`, `COVERAGE_FLOOR=80`).
- `parity/discogs_truth_tables.php`; `classes/discogs/testdata/php_mapper.json`, `php_input_parser.json`, `php_scorer.json`, `php_price_suggestion.json`.
- Tests: `TestPHPTruthDiscogs`, `TestPhase14Threats`, `TestPhase14Edges`, `TestPhase14Jobs`, `TestRouteTablePhase14` (complete), `FuzzWriteEndpoints` (Phase 14 routes), coverage tests per package, `TestFeedbackEdges`, `TestGolemAdminSchemas`.
- Evidence: `14-SECURITY-REVIEW.md`, validated `14-VALIDATION.md`, REQUIREMENTS statuses, todos moved to done.
## Assumptions
- The gate lives in summercms.go/scripts and runs application commands in the sibling `../fonoteka.go`, as check-phase13.sh does; the two plugin submodules are tested through the application workspace.
- Live vendor calls are never part of the gate (D-15); the manual-only rows of 14-VALIDATION.md stay manual.
Task 1: One command proves the whole phase green or red: check-phase14.sh runs both repositories, the corpus and the named tests
scripts/check-phase14.sh
scripts/check-phase13.sh (whole file), .planning/phases/14-domain-jobs-and-external-integrations/14-VALIDATION.md, ../fonoteka.go/parity/parity_test.go (expectedPHPRoutes, expectedPortedRoutes), ../fonoteka.go/parity/check_corpus.go
Copy check-phase13.sh's structure into scripts/check-phase14.sh with `PHASE14_*` overrides, `PHASE_DIR` pointing at the Phase 14 directory, `APP_PLUGINS=(./plugins/golem15/fonoteka/... ./plugins/golem15/user/... ./plugins/golem15/golem/... ./plugins/golem15/feedback/...)`, `EXPECTED_ROUTES=175`, `EXPECTED_PORTED=172`, `EXPECTED_PENDING=3`, `COVERAGE_FLOOR=80`, and the python detector renamed `phase14_detect` with the same exit codes. Wire `--go` (vet and `go test ./... -count=1` in summercms.go; vet and `go test ./... -count=1 -race` over APP_PLUGINS plus `./app/... ./parity/...` in fonoteka.go), `--parity` (TestParityCorpus with the three counts read from its output and from the manifest, every TestFonotekaNuxtFlows subtest, TestBroadcastGoldens, TestUpstreamSidecarsAreReplayed, check_corpus `--require-recorded --check-secrets`, TestDocsTree, docs:build --check) and `--self-test` (each detector fails on a planted failing, skipped, zero-test, racy and non-JSON input, and a planted pending count of 4 fails `--parity`). Tasks 3 and 4 add the `--named`, `--removal`, `--coverage` and `--evidence` stages to this script; `--all` runs every stage the script defines except `--removal`, which edits tracked source and runs on its own (the Phase 13 precedent). An unknown flag prints usage and exits 2.
bash -n scripts/check-phase14.sh && bash scripts/check-phase14.sh --self-test && bash scripts/check-phase14.sh --go && bash scripts/check-phase14.sh --parity
Non-zero exit from any stage; the self-test reports a detector that did not fail on its planted input; --parity reports counts other than 175 recorded, 172 ported and passing, 3 pending, or any failing flow, sidecar, secret or docs check.
- `grep -c 'EXPECTED_PORTED=172' scripts/check-phase14.sh` and `grep -c 'EXPECTED_PENDING=3' scripts/check-phase14.sh` each print 1.
- `grep -c 'plugins/golem15/golem/\.\.\.' scripts/check-phase14.sh` and `grep -c 'plugins/golem15/feedback/\.\.\.' scripts/check-phase14.sh` each print at least 1.
- `bash scripts/check-phase14.sh --bogus` exits 2 and prints the usage text.
The gate runs end to end over both repositories and the corpus with exact counts, and fails closed.
Task 2: The Discogs rules match PHP row for row, and every Phase 14 application package is fully covered at its edges
../fonoteka.go/parity/discogs_truth_tables.php, ../fonoteka.go/parity/README.md, ../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/testdata/, ../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/php_truth_test.go, ../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/coverage_test.go, ../fonoteka.go/plugins/golem15/fonoteka/phase14_edges_test.go, ../fonoteka.go/plugins/golem15/fonoteka/phase14_jobs_test.go, ../fonoteka.go/plugins/golem15/fonoteka/write_endpoints_fuzz_test.go, ../fonoteka.go/plugins/golem15/fonoteka/testdata/fuzz/FuzzWriteEndpoints/, ../fonoteka.go/plugins/golem15/fonoteka/routes_table_phase14_test.go, ../fonoteka.go/plugins/golem15/fonoteka/console/phase14_commands_test.go, ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/phase14_controllers_test.go, ../fonoteka.go/plugins/golem15/fonoteka/classes/phase14_classes_test.go
../fonoteka.go/parity/csv_truth_tables.php, /media/nvme/dev/golem15/fonoteka/plugins/golem15/fonoteka/classes/discogs/{DiscogsMapper,DiscogsInputParser,ReleaseMatchScorer,PriceSuggestionResolver}.php, /media/nvme/dev/golem15/fonoteka/plugins/golem15/fonoteka/tests/unit/{DiscogsMapperTest,DiscogsInputParserTest,ReleaseMatchScorerTest,PriceSuggestionResolverTest,DiscogsCandidateMapperTest}.php, ../fonoteka.go/plugins/golem15/fonoteka/write_endpoints_fuzz_test.go, ../fonoteka.go/plugins/golem15/fonoteka/routes_table_phase14_test.go, ../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/*.go, the 14-02 to 14-05 SUMMARY files (edge truths and test names)
Per lean-mode rule 3 and the edge truths of plans 14-02 to 14-05.
(1) parity/discogs_truth_tables.php follows csv_truth_tables.php: PHP_ROOT from the environment, require_once the four classes, stub what they need, and write php_mapper.json (mapRelease, mapSearchResult, mapMasterVersion over the PHP unit-test fixtures plus edge releases: missing images, multiple formats, unicode artists, master releases), php_input_parser.json (URLs, ids, barcodes with and without check digits, garbage), php_scorer.json and php_price_suggestion.json (every currency, empty suggestions, rounding ties) into classes/discogs/testdata. php_truth_test.go `TestPHPTruthDiscogs` replays every row and compares JSON bytes. README documents the regeneration command.
(2) Coverage to at least 80%: classes/discogs (coverage_test.go for client error branches, limiter store errors, applicator and cover fetcher branches), console (phase14_commands_test.go), and every Phase 14 function in the root (workers, wiring), classes (album_recognition, CSV write-service variants, WR-02 paths) and controllers/api (match, apply, import, cover-price, credential tests, recognize) packages by `go tool cover -func`.
(3) phase14_edges_test.go `TestPhase14Edges`: limiter 50/51 and budget 15/16 s, Retry-After absent/non-numeric/numeric, MAX_CANDIDATES 10/11, prune cutoff at exactly 90 days and one second older, inbound limits 60/61, 20/21, 10/11 and the token cover-price throttle 12/13; phase14_jobs_test.go `TestPhase14Jobs` (cancel during a paused match, resume after re-dispatch writes the same rows, import of an already written row is skipped). routes_table_phase14_test.go completes `TestRouteTablePhase14` for the eleven fonoteka routes. write_endpoints_fuzz_test.go adds the Phase 14 write routes to `FuzzWriteEndpoints` with a committed seed corpus.
go -C ../fonoteka.go vet ./... && go -C ../fonoteka.go test ./plugins/golem15/fonoteka ./plugins/golem15/fonoteka/classes/discogs -count=1 -race -v -run '^(TestPHPTruthDiscogs|TestPhase14Edges|TestPhase14Jobs|TestRouteTablePhase14|FuzzWriteEndpoints)$' && go -C ../fonoteka.go test ./plugins/golem15/fonoteka/... -count=1 -cover
Any command exits non-zero; a verbose run prints "--- FAIL", "no tests to run", "--- SKIP" or "DATA RACE", or lacks "--- PASS" for TestPHPTruthDiscogs, TestPhase14Edges, TestPhase14Jobs, TestRouteTablePhase14 and FuzzWriteEndpoints; the cover run reports below 80.0% for classes/discogs or console.
- `ls ../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/testdata/php_mapper.json ../fonoteka.go/plugins/golem15/fonoteka/classes/discogs/testdata/php_price_suggestion.json` succeeds.
- `grep -c 'PriceSuggestionResolver' ../fonoteka.go/parity/discogs_truth_tables.php` prints at least 1.
- `ls ../fonoteka.go/plugins/golem15/fonoteka/testdata/fuzz/FuzzWriteEndpoints/ | wc -l` is larger than before this task.
The Discogs rules are pinned to PHP's own output and the application side of the phase is covered at its edges.
Task 3: Each Phase 14 threat has a test that fails without its protection, and the framework and both new plugins are fully covered
modules/fetchguard/client_coverage_test.go, modules/tide/upstream_coverage_test.go, modules/sunscreen/sunscreen_coverage_test.go, modules/beachcomber/typesense/engine_test.go, ../fonoteka.go/plugins/golem15/fonoteka/phase14_security_test.go, ../fonoteka.go/plugins/golem15/golem/classes/services/coverage_test.go, ../fonoteka.go/plugins/golem15/golem/classes/providers/coverage_test.go, ../fonoteka.go/plugins/golem15/golem/golem_admin_test.go, ../fonoteka.go/plugins/golem15/golem, ../fonoteka.go/plugins/golem15/feedback/feedback_edges_test.go, ../fonoteka.go/plugins/golem15/feedback/classes/coverage_test.go, ../fonoteka.go/plugins/golem15/feedback, scripts/check-phase14.sh, .planning/phases/14-domain-jobs-and-external-integrations/14-SECURITY-REVIEW.md
scripts/check-phase13.sh (removal_table, removal_harness, run_named, coverage_report, func_floor), .planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-SECURITY-REVIEW.md (format), the threat_model blocks of 14-01-PLAN.md to 14-05-PLAN.md, ../fonoteka.go/plugins/golem15/fonoteka/phase13_security_test.go, modules/fetchguard/client.go, modules/tide/upstream.go, modules/tide/upstream_proxy.go, modules/sunscreen/sunscreen.go
Per security enforcement (ASVS level 1, block on high).
(1) Coverage at 80% or more for modules/fetchguard, modules/tide, modules/sunscreen, modules/beachcomber and modules/beachcomber/typesense (new *_coverage_test.go files for remaining branches: transport errors, cap edges, proxy forward-mode refusal paths, CA reuse errors, redaction of LogValuer groups), and for every package of sm-golem-plugin (services, providers, security, factories, valueobjects, models, console, controllers; `TestGolemAdminSchemas` compiles the admin YAML under cabana) and sm-feedback-plugin (classes, controllers/api, models, console; `TestFeedbackEdges` pins each validation limit one step either side). Plugin tests are committed and pushed in each checkout, then each pointer is bumped in fonoteka.go in its own commit.
(2) phase14_security_test.go `TestPhase14Threats`: one subtest per mitigated application threat (T-14-08 to T-14-36) that drives the real handler or worker and asserts the protection; framework threats (T-14-01 to T-14-07) map to their named module tests. check-phase14.sh `--named` requires every test named in 14-VALIDATION.md and the security review by exact name; `--removal` gets a `removal_table` row per mitigated threat (anchor-exact mutation of the protecting line, the named test that must fail on an assertion, cmp restore; refuses files with uncommitted changes); `--coverage` enforces the package floors and the per-function floors (`go tool cover -func`) for the root, classes and controllers/api Phase 14 functions.
(3) 14-SECURITY-REVIEW.md maps every T-14 id (including T-14-SC and the accepted T-14-28) to category, severity, disposition, protecting file and the named test seen failing under `--removal`, with the canon referrals of plans 14-02 to 14-05 listed as covered by this review.
go vet ./... && go test ./modules/fetchguard ./modules/tide ./modules/sunscreen ./modules/beachcomber/... -count=1 -race -cover && go -C ../fonoteka.go test ./plugins/golem15/fonoteka -count=1 -race -v -run '^(TestPhase14Threats)$' && go -C ../fonoteka.go test ./plugins/golem15/golem/... ./plugins/golem15/feedback/... -count=1 -race -cover && bash scripts/check-phase14.sh --named && bash scripts/check-phase14.sh --coverage && bash scripts/check-phase14.sh --removal
Any command exits non-zero; a cover line below 80.0% for a listed package; the verbose run prints "--- FAIL", "no tests to run", "--- SKIP" or "DATA RACE", or lacks "--- PASS: TestPhase14Threats"; --removal reports a mutation whose named test still passes or a file that does not restore byte for byte.
- `grep -c 'T-14-' .planning/phases/14-domain-jobs-and-external-integrations/14-SECURITY-REVIEW.md` prints at least 37.
- `grep -cE '^run_(named|removal|coverage)\(\)' scripts/check-phase14.sh` prints 3.
- `git -C ../fonoteka.go/plugins/golem15/golem status --porcelain --branch` and the same for feedback show no "ahead".
Framework, golem and feedback code are fully covered and every threat is proven by a test that fails without its protection.
Task 4: The phase record is complete: validation signed off, requirements marked, API coverage checked, folded todos closed, and the whole gate green
scripts/check-phase14.sh, .planning/phases/14-domain-jobs-and-external-integrations/14-VALIDATION.md, .planning/phases/14-domain-jobs-and-external-integrations/COVERAGE.md, .planning/REQUIREMENTS.md, .planning/todos/pending/fetchguard-guarded-http-client.md, .planning/todos/pending/redacting-slog-handler.md, .planning/todos/done/fetchguard-guarded-http-client.md, .planning/todos/done/redacting-slog-handler.md
.planning/phases/14-domain-jobs-and-external-integrations/14-VALIDATION.md, .planning/phases/14-domain-jobs-and-external-integrations/COVERAGE.md, .planning/REQUIREMENTS.md, .planning/ROADMAP.md (Phase 14 section), scripts/check-phase13.sh (evidence_check), the 14-01 to 14-05 SUMMARY files
Per D-06, D-07, D-13, D-14 and the API coverage contract.
(1) `--evidence` in check-phase14.sh refuses: REQUIREMENTS.md still containing the SDK wording for INTG-02 or "sitemap output" in API-08; a ROADMAP Phase 14 `**Repos:**` line plus success-criteria block (the lines between `**Success Criteria**` and `**Plans:**`, not the plan list) missing `albums/import/discogs`, `recognize`, `sm-golem-plugin` or `sm-feedback-plugin`; any 14-VALIDATION.md row that is pending, TBD or names a test that did not pass in this run; frontmatter without `nyquist_compliant: true` and `wave_0_complete: true`; a COVERAGE.md INTEGRATE row whose named test is missing or did not pass, or an OPT-OUT row without a reason; a security review row without a test. `--all` runs every stage except `--removal`.
(2) Planning docs (one commit in summercms.go, planning files only, Edit not Write for existing files): 14-VALIDATION.md gets real task ids (14-01-T1 … 14-06-T4) in its Per-Task Verification Map, the measured coverage numbers, status green per row, `status: validated`, `nyquist_compliant: true`, `wave_0_complete: true`; REQUIREMENTS.md marks JOBS-02, JOBS-03, SRCH-02, INTG-01, INTG-02, API-08 and CLI-05 Complete in both the checklist and the traceability table; COVERAGE.md gets the named test per INTEGRATE row; `git mv` the two folded todos from pending to done.
(3) Run `bash scripts/check-phase14.sh --all` and record its final summary line in 14-VALIDATION.md.
bash scripts/check-phase14.sh --evidence && bash scripts/check-phase14.sh --all && grep -q 'nyquist_compliant: true' .planning/phases/14-domain-jobs-and-external-integrations/14-VALIDATION.md && test -f .planning/todos/done/fetchguard-guarded-http-client.md && test -f .planning/todos/done/redacting-slog-handler.md
Non-zero exit from --evidence or --all; the validation file lacks `nyquist_compliant: true`; either folded todo is not in .planning/todos/done.
- `grep -cE '^\| (JOBS-02|JOBS-03|SRCH-02|INTG-01|INTG-02|API-08|CLI-05) \| Phase 14 \| Complete' .planning/REQUIREMENTS.md` prints 7.
- `grep -cE '^run_evidence\(\)' scripts/check-phase14.sh` prints 1 and the `--all` branch calls run_evidence.
- `grep -cE '^\|.*\| ⬜ pending \|$' .planning/phases/14-domain-jobs-and-external-integrations/14-VALIDATION.md` prints 0.
- `ls .planning/todos/pending/ | grep -cE 'fetchguard-guarded-http-client|redacting-slog-handler'` prints 0.
Phase 14 has a green gate, a signed-off validation map, complete requirements and a decided API coverage matrix.
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| Gate script → tracked source | The removal harness edits and restores tracked files |
| Gate verdict → phase sign-off | A false green would close the phase with gaps |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-14-37 | Repudiation | gate counts | medium | mitigate | Exact 175/172/3 counts from test output and manifest, zero-test and skip detectors, planted-input self-test (Task 1). |
| T-14-38 | Tampering | --removal harness | medium | mitigate | Refuses files with uncommitted changes, anchor-exact edits, cmp restore after each mutation, and runs only on its own flag, never inside --all (Tasks 1 and 3). |
| T-14-SC | Tampering | package installs | low | accept | No package installs; PHP truth tables run the existing PHP checkout. |
- `bash scripts/check-phase14.sh --all` green in summercms.go.
- Both plugin submodules pushed with no local commits ahead; fonoteka.go pointers committed.
- Full unit coverage of all Phase 14 code; PHP truth tables for the Discogs pure classes.
- Every T-14 threat proven by a failing-without-protection test; gate, validation, requirements and API coverage evidence complete.