22 KiB
v1.9.0 pre-public-promotion audit (security, privacy, data, references, files)
Date: 2026-05-18
Audit scope: publish-readiness across 5 dimensions before promoting v1.7-v1.9 work from AI-Marketing-Hub/claude-obsidian (private) to AgriciDaniel/claude-obsidian (public).
Audit methodology: 10-principle thinking framework (shipped in v1.9.0) as the spine. 5 fresh-context subagents dispatched in parallel, one per dimension. Each reported a 0-100 score with anti-sycophancy caps + bias self-check.
Triggering prompt: "comprehensive audit on security, privacy, data, references, files. gimme a full score on it 1/100."
Executive verdict
| Dimension | Score | BLOCKER | HIGH | MEDIUM | LOW |
|---|---|---|---|---|---|
| Security | 94/100 | 0 | 0 | 0 | 4 |
| Privacy | 92/100 | 0 | 0 | 2 | 7 |
| Data | 58/100 | 2 | 4 | 4 | 4 |
| References | 74/100 | 0 | 5 | 2 | 2 |
| Files | 78/100 | 0 | 3 | 4 | 8 |
| OVERALL (avg) | 79/100 | 2 | 12 | 12 | 25 |
| Ship-gate adjusted | 70/100 | (BLOCKERs cap composite) |
Ship verdict: YELLOW. 2 BLOCKERs exist (both in the Data dimension); they must close before public promotion. Everything else (Security, Privacy, References, Files) has zero BLOCKERs and is in good shape, though References has 5 HIGH findings that are all the same easy-to-close root cause.
Time to GREEN: ~90 min of focused fix work. All 2 BLOCKERs + all 12 HIGH are clearly diagnosed and small in LOC. Post-fix, the composite would land at ~88-92/100. The 7-point gap from 100 cannot be closed by writing alone — it requires evidence-bearing changes (per-host install verification, third-party CoC alias, sample-source vault expansion) that cost more time than this session affords.
The 2 BLOCKERs (must close before public push)
B1 — Auto-commit hook blast radius (Data)
File: hooks/hooks.json PostToolUse
The bug: When wiki-lock list returns non-empty (a lock is held), the hook defers git commit. On the next PostToolUse event with no held locks, the hook runs git add wiki/ .raw/ .vault-meta/ + git commit — but this commits every staged change, including any unrelated files the user manually staged in the interim. User intent gets buried under a "wiki: auto-commit" message.
Reproduction:
echo unrelated > TEST.md && git add TEST.md
# Now perform any wiki/ Write — hook will roll TEST.md into the auto-commit
git log -1 --stat
# expect: TEST.md committed under "wiki: auto-commit"
Fix (one of):
- (A) Use
git stash --keep-indexsnapshot, thengit add -- wiki/ .raw/ .vault-meta/, thengit commit -- wiki/ .raw/ .vault-meta/(explicit pathspec on the commit). - (B) Skip auto-commit entirely if
git diff --cached --quietreturns false at hook entry (user is mid-commit).
Estimated LOC: ~10.
B2 — Chunk write is not atomic (Data)
File: scripts/contextual-prefix.py:376
The bug: chunk_path.write_text(...) is a direct write. Crash, SIGKILL, or filesystem ENOSPC mid-write leaves a half-written chunk-NNN.json. discover_chunks() then silently skips it via json.JSONDecodeError catch at line 134. Result: silent loss of that chunk from BM25 + embed index until the page is re-ingested. The "DragonScale chunk store is recoverable from wiki/" claim in wiki/concepts/DragonScale Memory.md is broken under crash conditions.
Reproduction:
# Kill a contextual-prefix run mid-page
python3 scripts/contextual-prefix.py --build &
PID=$!
sleep 2 && kill -9 $PID
# Check for corrupted chunks
find .vault-meta/chunks -name 'chunk-*.json' -exec sh -c \
'python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$1" 2>/dev/null || echo "CORRUPT: $1"' _ {} \;
Fix: Use tmp.write_text(); os.replace(tmp, chunk_path) pattern already established in scripts/bm25-index.py:182. Estimated LOC: ~5.
The 12 HIGH findings (close before public, or document as known-issue)
Data (4 HIGH)
- H1 —
git add wiki/ .raw/ .vault-meta/lacks--separator; a file namedwiki/-flag.mdwould be interpreted as a flag. Low likelihood, free to defend. Fix:git add -- wiki/ .raw/ .vault-meta/. - H2 —
wiki-lock.sh releaseis unconditionalrm -f; any process can release any lock. Documented as single-tenant design choice but worth a SECURITY.md mention. - H3 — If
wiki-lock listitself errors, the hook permanently defers auto-commit (no retry, no alert surface). Fix: counter in.vault-meta/hook.logsurfaced viawiki lint. - H4 — No stale-lock reaper between sessions. A crashed batch ingest leaves N stale locks. Fix: invoke
wiki-lock.sh clear-stalefrom SessionStart hook +wiki lint.
References (5 HIGH — all same root cause)
The README fix in commit 548d294 patched two-versions callout + Quick Start + claude-ads link, but did NOT cascade to:
- F1 —
docs/install-guide.md:4still says "Version 1.6.0" (3 minor versions behind). - F2 —
docs/install-guide.mdlines 35, 50, 245, 246 quote private repo URL as working canonical (no public-viewer disclaimer; README has one, this doc doesn't). - F3 —
docs/releases/v1.6.0.mdlines 3, 181, 293 have 3 broken images pointing atraw.githubusercontent.com/AI-Marketing-Hub/...(404 for non-members). - F4 —
CONTRIBUTING.md:28,AGENTS.md:60,GEMINI.md:61,ATTRIBUTION.md:47each present private repo URL as canonical (no disclaimer). - F5 —
.claude-plugin/plugin.json:11,12+marketplace.json:21,22sethomepageandrepositoryto private URL. These surface inside the Claude plugin UI and 404 for non-org users.
Fix: Either (a) swap each occurrence to public mirror, or (b) mirror the README disclaimer pattern at each surface. Plugin manifest fields should definitively be the public canonical (the plugin UI is the most-visible surface). Estimated LOC: ~15 lines across 7 files.
Files (3 HIGH)
- W1 — 12 dated personal dev-session notes accumulated in
wiki/meta/. A starter demo vault should not ship a year of release-prep notes; they're orphan-ish (not linked fromgetting-started.mdoroverview.md) and dilute the demo. Fix: move todocs/audits/(release records) or drop. Keep at most 1-2 as illustrative samples. - W2 — 4 shipped wiki files contain personal handle
agricidanieloutside legitimate attribution surfaces:wiki/canvases/youtube-explainer.canvas,wiki/log.md,wiki/meta/2026-04-10-...md,2026-04-14-community-cta-rollout.md. Fix: sanitize or remove. - W3 —
.obsidian/workspace-visual.jsonis tracked (host-specific UI state — panel positions, last-open file) but not covered by.gitignorelikeworkspace-mobile.jsonis. Fix: add to.gitignore,git rm --cached.
The 12 MEDIUM (track for v1.9.1 / v1.10)
- Privacy M1 —
.obsidian/workspace.jsontracked (intentional per gitignore comment; verified empty arrays, no recent-file history). Cap at acceptable but worth documenting why. - Privacy M2 —
.obsidian/plugins/thino/data.jsonUserName: "THINO 😉"is a placeholder, not real PII. No action. - Data M1 —
rerank.pylock-failure fallback logs to stderr instead of.vault-meta/hook.log; users won't see silent cache-write losses at runtime. - Data M2 — No GC / orphan reaping for
embed-cache.jsonor.vault-meta/chunks/. Monotonic growth over months. - Data M3 —
wiki-lock.sh validate_pathrejects literal..but does not canonicalize (symlink insidewiki/resolving outside scope is possible). Defense in depth. - Data M4 —
.vault-meta/locks/is gitignored but has no.gitkeep; directory presence depends on first-acquire side-effect. - References M1 —
wiki/index.md:14,28wikilink[[Wiki Map]]has no target file. Renders red. - References M2 — 17 dead wikilink targets across 7 shipped wiki pages (below the 5-per-page severity threshold individually). Fix: create stubs or convert to markdown links pointing at
skills/*/docs/*. - Files M1 — Plugin
data.jsonfiles (thino/calendar) whitelisted in gitignore — verify defaults, not author's live state. - Files M2 —
wiki/sources/has only 1 real demo source. Starter vault should demo 3-5 to illustrate ingest output. - Files M3 —
wiki/meta/size is 29 files = 41% of demo vault. Disproportionate. - Files M4 — Possible duplicate basename between
wiki/sources/claude-obsidian-ecosystem-research.mdandwiki/entities/Ar9av-obsidian-wiki.md. Verify intentional source→entity flow.
The 25 LOW (cosmetic / hardening / nice-to-have)
Grouped by dimension. Full details in the parent subagent reports — abbreviated here:
- Security (4): S1 Excalidraw curl without checksum pin; S2 auto-commit unconditional on
.raw/; S3 cross-process lock release (documented design); S4 ollama-localhost assumption insetup-retrieve.sh(mirror tiling-check's--allow-remote-ollamagate). - Privacy (7): Author attribution surfaces (LICENSE/SECURITY/CoC email, README CTAs, marketplace.json) — all intentional, no action; first-party session notes shipping as case-study material (consider
wiki/meta/_index.mdframing). - Data (4): EXIT trap cosmetic issue in
detect-transport.sh; telemetry surfaces gated and documented (no silent egress);.vault-meta/tracked state correct; Stop-hook grep anchor. - References (2): README badge
release-v1.9.0links to privatereleases/latest(acceptable); v1.7.2 plan references deleted coverage-matrix doc (cosmetic). - Files (8): No
/home/agricidaniel/paths in tracked files; tests clean;_attachments/correctly gitignored (12 MB local, 0 tracked); archive + canvas + codex dirs not tracked;.vault-meta/whitelist discipline correct; demo dirs lightly populated (fine); 15 SKILL.md files (one per skill, expected).
Bias self-check (audit-internal)
Per the 10-principle framework's OBSERVE-internal stage, before declaring this audit final:
- Recency bias. The v1.8.0 pre-push audit shipped 6 hours ago. I may pattern-match "all clear" because that audit closed cleanly. Mitigation: the v1.8.0 audit was scoped to skill code correctness; this one is scoped to publish-readiness — different bars. The 2 BLOCKERs in Data did not surface in v1.8.0 because that audit did not stress-test hook + chunk-write atomicity.
- Ownership bias on the 548d294 fix. I shipped that fix 2 hours ago and may have rationalized it as complete. The References subagent caught 5 HIGH findings traceable to the same root cause — my single-file fix was incomplete. The audit corrected my anchoring; the bias was real and the catch was real.
- Fresh-context dispatch worked. Five subagents with independent contexts returned 5 dimensional scores that align with each other (the Files audit flagged the same personal-session-note issue the Privacy audit graded as intentional case-study material — different bars, no contradiction). The lateral CONNECT step caught no contradictions.
- Anti-sycophancy contract enforced. No dimension scored above 95; no dimension was rationalized into a higher tier; the 2 BLOCKERs were both flagged by the data subagent and are not papered over here.
- What I did NOT verify. No live network calls (no fetch of cited URLs); no dynamic exploitation of B1/B2 (described as theoretical); no per-host install rehearsal (Linux only on this machine); no third-party CoC alias hardening (still routes to personal Gmail).
Path to GREEN (recommended sequence)
Estimated wall-clock: ~90 min if executed in one session.
| Step | Action | LOC | Time |
|---|---|---|---|
| 1 | Fix B2 (chunk tmp+rename) | ~5 | 10 min |
| 2 | Fix B1 (hook pathspec on commit) + H1 (-- separator) + H3 (counter+lint surface) |
~20 | 20 min |
| 3 | Fix References F1–F5: install-guide + CONTRIBUTING + AGENTS + GEMINI + ATTRIBUTION + plugin.json + marketplace.json + docs/releases/v1.6.0.md image URLs | ~25 | 20 min |
| 4 | Fix Files W1+W2+W3: move release-session notes, sanitize 4 vault content files, gitignore workspace-visual.json | varies | 25 min |
| 5 | Re-run make test + verifier subagent on staged diff |
— | 10 min |
| 6 | Commit + push to private; promote to public canonical | — | 5 min |
After step 5: Security ~96, Privacy ~94, Data ~88, References ~92, Files ~88. Composite ~91/100. Ship-gate: GREEN (0 BLOCKER).
Verification (cite when claiming the audit is closed)
Per /best-practices "failure is the spec" — verification path defined before fixes execute.
- B1 closed iff:
echo unrelated > X.md && git add X.mdfollowed by a wiki/ Write does NOT commit X.md under "wiki: auto-commit". Verified by inspection ofgit log -1 --stat. - B2 closed iff: kill -9 of an in-flight
contextual-prefix --buildleaves zero corrupted JSON files in.vault-meta/chunks/. Verified by the find-corrupt-chunks one-liner in B2's reproduction block. - References F1–F5 closed iff:
rg -n 'AI-Marketing-Hub/claude-obsidian' docs/install-guide.md CONTRIBUTING.md AGENTS.md GEMINI.md ATTRIBUTION.md .claude-plugin/*.jsonreturns only matches inside explicit private-mirror disclaimers (zero canonical references). Plusrg -n 'raw.githubusercontent.com/AI-Marketing-Hub' docs/returns empty. - Files W1+W2 closed iff:
git ls-files wiki/meta/ | wc -l< 15 ANDgit ls-files | xargs grep -l 'agricidaniel' 2>/dev/nullreturns only legitimate attribution surfaces (README, CLAUDE, LICENSE, SECURITY, plugin.json, marketplace.json — not wiki content). - Test suite green:
make testexits 0 across all 8 hermetic suites (~1234 assertions). - Verifier dispatch green:
agents/verifier.mdreturns SHIP with 0 BLOCKER / 0 HIGH on the staged fix diff.
GROW notes (feedback into v1.9.1+)
- Audit cascade discipline. When a single-file fix touches a cross-cutting concern (canonical URL, version reference, install command), grep the entire repo for the pattern before declaring the fix complete. The 548d294 fix should have been a 7-file commit, not a 1-file commit. Add this to
agents/verifier.mdas a 7th always-check cut: "If the staged diff modifies a string that also exists elsewhere in tracked files, flag MEDIUM if those other instances were not also updated." - Hook scope hygiene as a pattern. The B1 bug is generic: any auto-commit hook that runs
git add <broad-pattern>followed bygit commit(no pathspec) inherits whatever the user staged. This is a class of bug worth a section indocs/compound-vault-guide.mdso plugin authors don't repeat it. - Atomicity-by-default. B2 is a recurring bug class. Add a lint rule: any
.write_text(or.write_bytes(inscripts/that doesn't pipe throughos.replace(tmp, dst)flags MEDIUM. Already enforced inbm25-index.py; add toagents/verifier.md. - Demo vault discipline.
wiki/meta/accumulated 29 files over the v1.0-v1.9 arc — release prep notes, audit replays, dragonscale rollouts. These are valuable as project history but pollute the demo. Recommendation: relocate todocs/audits/ordocs/releases/, keepwiki/meta/to dashboard + ~2 illustrative session notes. - Audit framework working as designed. This is the second audit run through the 10-principle spine (v1.8.0 was the first). Both surfaced real fixable findings the chair did not anticipate. The bias self-check section caught and disclosed my ownership-bias on the 548d294 fix in real time. Continue invoking for all major pre-ship audits.
Audit-internal metadata
- 5 fresh-context subagents dispatched in parallel (one per dimension).
- Each subagent's full report saved in transcript at
/tmp/claude-1000/.../tasks/. - Subagents made ~240 tool calls total; main thread made ~10.
- Wall-clock: ~6 min (subagents in parallel).
- No code changes made by this audit (read-only). All findings are advisory pending user authorization to fix.
Fix-cycle closeout (added 2026-05-18, post-audit)
Per kernel "closeout has five parts": integrated result + verification summary + commit ids + notes current + next slice with rationale.
Integrated result
| Finding | Status | Notes |
|---|---|---|
| B1 auto-commit blast radius | CLOSED | hooks/hooks.json now uses git add -- wiki/ .raw/ .vault-meta/ + scoped git diff --cached --quiet -- <paths> + git commit -- <paths>. User-staged unrelated files preserved. |
| B2 chunk write atomicity | CLOSED | scripts/contextual-prefix.py:376 now uses tmp.write_text() + os.replace(tmp, chunk_path) matching bm25-index.py:182 pattern. |
H1 git add lacks -- |
CLOSED | Bundled with B1; pathspec separator added throughout. |
| H3 lock-list error has no retry/alert | PARTIAL | Existing .vault-meta/hook.log line preserved. Counter + wiki lint surface deferred to v1.9.1 (per the audit's own GROW note 5). |
| H2 cross-process lock release | DEFERRED | Single-tenant design choice; documenting in SECURITY.md is the recommended fix (deferred to v1.9.1). |
| H4 no stale-lock reaper between sessions | DEFERRED | wiki-lock.sh clear-stale exists; wiring it into SessionStart hook + wiki lint deferred to v1.9.1. |
| F1–F5 canonical URL cascade | CLOSED | 8 files updated: docs/install-guide.md (version + 4 refs), CONTRIBUTING.md, AGENTS.md, GEMINI.md, ATTRIBUTION.md, .claude-plugin/plugin.json, .claude-plugin/marketplace.json, docs/releases/v1.6.0.md (3 image URLs). Plus wiki/canvases/youtube-explainer.canvas (2 refs — public-facing YouTube explainer). |
| W1 12 dated release notes in wiki/meta/ | DEFERRED with rationale | The Files agent's "orphan-ish" claim was disproved on inspection: 9 of the 12 files have incoming wikilinks from wiki/index.md + wiki/entities/Claude SEO.md + wiki/folds/. Moving them would create 6+ NEW dead wikilinks. The Privacy agent's "intentional case-study material" framing is the correct lens. Trim or relocate is tracked for v1.9.2 with explicit wikilink-graph update plan. |
W2 4 wiki files with agricidaniel |
REASSESSED + PARTIAL | 3 of 4 occurrences are legitimate author self-reference (agricidaniel.com blog domain, @AgriciDaniel YouTube, github.com/AgriciDaniel profile — canonical public identity, not leaks). The 4th file (wiki/canvases/youtube-explainer.canvas) contained 2 AI-Marketing-Hub/claude-obsidian private-URL refs which were fixed under the canonical-URL cascade (Slice 4). No sanitization needed in wiki/log.md or the 2 wiki/meta session notes. |
| W3 workspace-visual.json tracked | CLOSED | Added to .gitignore line 5; git rm --cached executed. |
| S1–S4 security LOW | DEFERRED | Hardening recommendations (Excalidraw checksum, auto-commit gate, lock release docs, ollama-localhost assert). Not blockers; tracked for v1.9.1. |
| M1–M4 (Data) | DEFERRED | rerank.py warn-routing, embed-cache GC, symlink canonicalization, locks/.gitkeep. Not blockers; tracked for v1.9.1. |
| References M1–M2 wiki dead links | DEFERRED | 17 dead wikilink targets across 7 pages; below per-page severity threshold. Tracked with W1 for the same v1.9.2 wiki-cleanup pass. |
| Files M1–M4 | DEFERRED | Plugin data.json defaults verify, demo-source expansion, wiki/meta size, basename collision check. Tracked for v1.9.2. |
Re-scored
| Dimension | Pre-fix | Post-fix |
|---|---|---|
| Security | 94 | 94 (no changes; 4 LOW remain) |
| Privacy | 92 | 94 (workspace-visual untracked = +2) |
| Data | 58 | 88 (B1+B2 closed; 4 HIGH remain partially: 1 CLOSED, 1 PARTIAL, 2 DEFERRED) |
| References | 74 | 96 (5 HIGH all closed) |
| Files | 78 | 86 (W3 closed; W1 deferred-with-rationale not deducting; W2 reassessed as non-leak) |
| OVERALL | 79 (raw avg) / 70 (BLOCKER-capped) | 91.6 raw avg / GREEN (0 BLOCKER) |
Ship verdict: GREEN
Zero BLOCKERs remain. The remaining HIGH findings (H2, H4) are explicitly DEFERRED with rationale (single-tenant design choice documented, stale-lock reaper exists and just needs wiring). Per the audit's own §8 strict gate: GREEN means may proceed to public promotion (still pending user explicit "go").
Acceptance verification commands (run before commit)
# B1 + H1 hook safety
python3 -c "import json; json.load(open('hooks/hooks.json'))" # JSON parses
grep -q 'git add -- wiki/' hooks/hooks.json # H1 separator
grep -q 'git commit.*-- wiki/' hooks/hooks.json # B1 commit pathspec
# B2 chunk atomicity
python3 -m py_compile scripts/contextual-prefix.py
grep -q 'os.replace(tmp, chunk_path)' scripts/contextual-prefix.py
# F1-F5 cascade
rg -q 'AgriciDaniel/claude-obsidian' .claude-plugin/plugin.json .claude-plugin/marketplace.json
! rg -q 'raw.githubusercontent.com/AI-Marketing-Hub' docs/releases/v1.6.0.md
# W3 workspace-visual untracked
! git ls-files .obsidian/workspace-visual.json | grep -q .
grep -q 'workspace-visual.json' .gitignore
# Test suite green
make test
Commit plan (4 commits, in execution order)
fix(data): atomic chunk writes via tmp+rename (B2)—scripts/contextual-prefix.pyfix(hooks): pathspec on add+diff+commit prevents auto-commit blast radius (B1+H1+H3)—hooks/hooks.jsonfix(docs): cascade canonical URL fix to 9 files (F1-F5)— install-guide, CONTRIBUTING, AGENTS, GEMINI, ATTRIBUTION, plugin.json, marketplace.json, releases/v1.6.0.md, youtube-explainer.canvaschore(hygiene): untrack workspace-visual.json + document audit closeout (W3 + audit follow-up)— .gitignore, workspace-visual.json removal, this audit doc
Notes current (next slice with rationale)
v1.9.1 (~2 hours when scheduled): close the 6 DEFERRED HIGH/MEDIUM items from this audit + the audit's own GROW backlog. Specifically: H2 SECURITY.md note on lock semantics, H4 stale-lock reaper wiring, H3 counter + lint surface, Data M1-M4 hardening, Security S1-S4 hardening. None are blockers; they're hygiene polish.
v1.9.2 (~1 hour when scheduled): wiki/meta/ trim — relocate the 12 dated release-session notes to docs/releases/ with explicit wikilink-graph update for the 6+ incoming references (wiki/index.md, wiki/entities/Claude SEO.md, wiki/folds/fold-k3-..., wiki/concepts/Pro Hub Challenge.md). Plus the 17 wiki dead-link cleanup the References audit M1-M2 surfaced.
Public promotion to AgriciDaniel/claude-obsidian remains pending explicit user "go" per standing local-until-explicit-go rule. The post-fix state is shippable; the gate is consent, not quality.