Lessons — KB-OS
Section titled “Lessons — KB-OS”Process lessons for the KB-OS ongoing project (the KB operating system: SMTM, dept structure, routing, dashboards). Captures what bit us and what to do differently. Portal: KB-OS.
Lessons
Section titled “Lessons”-
Structural routing changes must update every folder-scoped view in the same pass (SMTM v5.2, 2026-06-25): introducing dept-based task routing moved tasks into dept
Tasks/folders, but the_DASHBOARD.mdBases queries stayed pinned tofile.inFolder("Core/_WorkingOn/Tasks")— so dept-routed tasks silently vanished from the dashboard. → When you change where files live, sweep every query/dashboard/hook/skill that assumes the old location, in the same change. A folder move isn’t done until the views that read that folder are updated. -
Dogfood new structure on a real task immediately (2026-06-25): filing the SMTM-v5.2 task itself into
Core/Processes/Tasks/(the first task under the new dept-routing rule) is exactly what surfaced the broken dashboard. → Run a new convention on a real unit of work right away; the gaps show up in use, not in review. -
Vault-wide Bases queries: prefer a path pattern over enumerating folders (2026-06-25): with 20+
/Tasks/folders and growing,file.path.contains("/Tasks/")auto-covers new dept folders with zero maintenance, where anoroffile.inFolder(...)is brittle and needs editing for every new dept. (Caveat: Bases renders only in Obsidian — validate query changes visually in-app.) -
git mvdoesn’t stage content edits made after the move (2026-07-07): during the Ongoing-ProjectsCore/<Dept>/Projects/migration, several files gotgit mv-ed then had their content fixed (broken internal links, stale paths) — those edits sat unstaged until a follow-upgit statussweep caught them, and almost shipped in an incomplete commit. → After anygit mv+ content-edit sequence, re-rungit statusbefore committing — don’t assume the rename staged everything. -
Flat, project-named notes sidestep naming-collision debates for free (2026-07-07): consolidating scattered Ongoing-Project portals into
Core/<Dept>/Projects/, naming each note after its own project/repo (ai-config.md,monorepo.md,my_backup.md) rather than a uniform generic name (DASHBOARD.mdfor all) avoided the shared-folder naming collision that had previously blocked a uniform-naming attempt (KB-OS-portal-rename.md, 2026-07-06). → When multiple items need portal notes in one folder, name-by-identity beats name-by-role when the role name would collide. -
Templater corruption — open item, root cause unconfirmed (2026-07-07):
Templates/Folder Note.mdandTemplates/SMTM Task.mdwere found with their<% tp.file.title %>/<% tp.date.now(...) %>placeholders replaced by literal rendered values (“ai-config”, a date) — restored viagit checkout. Best guess: Templater fired against the template file itself rather than a target note, possibly during heavygit mvchurn while Obsidian had these files open/cached. Not reproduced — if it recurs, check whethertemplates_folder/enabled_templates_hotkeysin.obsidian/plugins/templater-obsidian/data.jsonpoint at a file that’s also open in the active pane during a folder restructure. -
Obsidian Bases: nested
and/orfilter groups are broken, not just unconfirmed (2026-07-07): aCore/DASHBOARD.md“Current Focus” query nested anor:(matching two path patterns) inside the top-leveland:list, to combine a recency filter with a two-path scope in one block. It rendered as a table (no visible error) but silently ignored thefile.mtimerecency filter — every matching file showed regardless of age. Splitting into two separate flatand:-only blocks, with the exact same date filter unchanged, fixed it immediately. → Never nest boolean groups in Bases filters; use one flat block per distinct path scope instead. Full gotcha entry:KB-OS-Usage.md. -
Isolate one variable at a time when a system fails silently (2026-07-07): the broken Current Focus query had two plausible bugs at once (a missing
status != "complete"filter, and the nestedor:above). Fixing both in the same pass would have left the date-arithmetic question genuinely unresolved either way. Fixing the status filter and un-nesting the boolean group together, then getting Talbot’s confirmation that it now worked, was still one round short of proof — only isolated for certain once the date filter itself was left completely unchanged across both the broken and fixed versions, making the nesting the only variable that changed. → When a silently-failing system has multiple candidate causes, change the minimum needed to test one hypothesis, and get explicit confirmation before claiming which fix worked. -
A “recently touched” view is self-defeating during a heavy-editing session (2026-07-07): a companion “Recently Touched Projects” Bases block (mtime, last 14 days) showed almost every project-adjacent file in the vault. Not a query bug — this same session’s own restructuring work (repeated
git mv, portal edits, JOB_DESCRIPTION updates) had bumpedmtimeacross most of/Projects/that same day. Removed the block; it had zero signal. → Recency-based “what’s active” views are least trustworthy right after (or during) a big batch-edit session — the editor’s own housekeeping pollutes the signal it’s trying to surface. -
A rename-for-consistency can silently multiply a duplicate-filename problem (2026-07-07): renaming
_DASHBOARD.md→DASHBOARD.mdvault-wide (Talbot’s explicit call, overriding an initial recommendation against it) brought the vault-wide count of files literally namedDASHBOARD.mdto 7 (Core, MBR, SDC, FSS, KB-OS, plus the 2 renamed). Every reference touched during the rename was made path-qualified, but this is now a standing risk for any future bare[DASHBOARD](/fss/dashboard/)link (first-occurrence-wins resolution — see the existing Focus-Pages precedent inCore/CLAUDE.md). → Before a rename that increases a filename’s existing occurrence count, flag the new collision surface explicitly — it’s a permanent characteristic of the vault from that point on, not a one-time migration cost. -
Templater file regex beats per-folder templates for any vault that grows (2026-07-08): 6 explicit folder-template entries in
data.json(one perTasks/folder) had to be updated manually every time a new dept folder was added. Switchingenable_file_templates: true+ regex.*/Tasks/.*covers all current and futureTasks/folders in one rule. → Prefer file regex over folder templates whenever the same template applies across a class of folders that will grow. -
One adaptive template +
tp.system.suggester()beats N specialized templates (2026-07-08): the temptation was to create 3 separate template files (Standalone / Repo-backed / KB-native). One file with a wizard is simpler: one SSOT, no divergence risk, no “which template do I pick?” decision for the user. → When variant behavior maps cleanly to a menu selection, use one file with a wizard rather than multiple files. -
tp.file.rename()in Templater enables auto-naming from a description prompt (2026-07-08): callingawait tp.file.rename(slug)during template execution renames the file before content is written. Combined withslug = desc.toLowerCase().replace(/\s+/g, "-"), this generates canonical<project>-<slug>filenames automatically — no manual file rename needed after creation. -
New Ongoing Project must register in 3 places; automate or one will be missed (2026-07-08): Ongoing-Projects.md registry, SMTM Smart template
repoProjectsarray, and KB portal note. The first two were originally manual — the smart template’s project list went stale immediately on the first new project. Fix:/setup-ongoing-projectStep 4 now writes all three in the same pass. → Any system with a hardcoded list that mirrors a registry must have the update automated in the same tool that creates the registry entry. -
Curated dashboard rows go stale the moment a source task closes — cross-check against
_active.mdbefore publishing (2026-07-09): the first draft of the Biggest Rocks table listed “ai-config-rules-upgrade Phase 1” as an active rock, sourced from a 3-day-old task file that described it as running — but_active.mdshowed the project completed and closed 2026-07-07. A curated (non-query) view trades silent-failure risk for staleness risk; its refresh step must include verifying each row’s source is still live. → When hand-curating any “what’s active/biggest” view, verify every row against the master tracker in the same pass. -
Verify archive completeness at commit time, not just move time (2026-07-09, mbr-content-consolidation):
mbr-cleanup.mdwas confirmed present inarchive/2026-07/right after the Phase-1mv, but had vanished by commit time (deleted outside the session — Obsidian or manual). Caught only by checking the committed tree (git ls-tree) against the decision table, then recovered from git history and re-archived in an amended commit. → An archive sweep isn’t verified until the commit tree is checked against the file list; the filesystem state at move time is not durable evidence. -
Check whether a brainstorm file already birthed an SSOT before extracting it line-by-line (2026-07-09):
MBR-Upgrades-CC.md(1993L) looked like a huge extraction job, but its own final responses show it createdFilter+Focus.md(all 53 rocks) andExecutive-Summary.md— so the right verification was a marker-grep of ~12 distinctive ideas against those SSOTs (10/12 hits; only “Lazy Mode” + one story detail were uncaptured). → For any mega-file, first ask “did this session’s output already get consolidated?” and verify by sampling markers — extraction effort should target the gap, not the file. -
Talbot’s inline edits to a presented decision table are first-class CEO directives (2026-07-09): approval came back partly as checkbox answers and partly as edits inside the Verdict column (“confirmed by Ta”, changed verdicts, “Move. See below (2)”, scope carve-outs like “but keep
Strategy/Notes/”). Reading only the## Talbot Responsesection would have missed several decisions. → When a decision table is the deliverable, diff the table itself on the next session — the table is a response surface, not static Claude output. -
Extraction passes must specifically hunt for the last unanswered Talbot Response (2026-07-09):
myMBR-OS.mdended with a Talbot Response (QuickBooks 2017→QBO migration, client-data privacy separation, what-data-lives-where) that never got a Claude reply — a real open infrastructure item buried for 3 months in a stale task file. Extracting only “ideas and decisions” would have skipped it because it was a question, not content. → When closing a stale task file, check whether the final Talbot Response was ever answered; unanswered questions route to the owning dept Inbox as open items. -
Promoting a project to “ongoing” activates the portal/task-location conventions — and can supersede an earlier ad-hoc verdict (2026-07-09): rate-scanner’s approved verdict (“relocate task to
Projects/rate-scanner/Tasks/”) was executed, then invalidated hours later when Talbot’s next directive made rate-scanner a registered ongoing project — the 2026-07-07 conventions then required dissolving_WorkingOn/Projects/rate-scanner/(ongoing ≠ finite folder) and moving the task to deptMBR/IT/Tasks/(repo-backed rule). → “Make X an ongoing project” is not just a registry row; walk the full convention chain (portal home, task location, live-state SSOT in repo), and when it overrides a previously-approved placement, flag the supersession explicitly rather than silently re-moving. -
Obsidian Bases
not:filter must be nested insideand:, not alongside it (2026-07-10):not:as a peer key toand:underfilters:causes a Bases semantic error (raw YAML is rendered instead of a table). When you need both,not:goes inside theand:list as a nested item:- not:\n - condition. Confirmed via Grok snippet — Bases does supportnot:, just not as a sibling toand:. -
Editing archive files bumps
file.mtimeinto time-window Bases queries (2026-07-10): Any file edit — includingsedfor checkbox fixes — updatesfile.mtime. If a Bases query filtersfile.mtime > now() - "14 days", bulk-editing old archive files pulls them into the active view. The wrong-layer fix (editing files) made the problem worse than the original. → Diagnose which query layer is matching before touching the files. -
file.path.contains("/Tasks/")matches/Tasks/archive/subfolders — always add exclusion (2026-07-10): Archive files live at.../Tasks/archive/..., so the standard task-listing filter also picks them up. Every task-listing Bases query needs an explicit- not:\n - file.path.contains("/archive/")inside itsand:block. Added toCore/DASHBOARD.mdandMBR/DASHBOARD.md. -
“MCP-primary” marketing ≠ MCP-only — check the CLI surface before treating a tool as a CLI>MCP violation (2026-07-13, KB-OS-memory-upgrade): CodeGraph is positioned MCP-first, and the initial review flagged it as a conflict with the standing CLI-over-MCP preference. But reading its CLI reference revealed
codegraph explorereturns identical output to thecodegraph_exploreMCP tool — so it was adopted CLI-only, no MCP server, no preference exception needed. Had the caveat been taken at face value the tool might have been rejected or forced through MCP unnecessarily. → Before accepting/rejecting a dual-use agent tool on its stated interface, verify the actual CLI surface (--help/ CLI docs); the marketing framing routinely hides a first-class CLI.
Escalation Candidates
Section titled “Escalation Candidates”- Structural routing changes must update every folder-scoped view in the same pass → promoted to global CLAUDE.md 2026-06-26 (“WHEN FILES MOVE, UPDATE EVERY VIEW THAT READS THEM”)
- Restructure audits must cover
.obsidian/plugin configs, not just skills/commands → promoted toai-config/AGENTS.md2026-07-07 (Skills Quality Standard section) - “MCP-primary” marketing ≠ MCP-only — verify the CLI surface first → promoted to
ai-config/AGENTS.md2026-07-13 (CLI vs MCP section)