Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ for tagged releases.
- Initial Knowledge Agent and Digital TeamMate framework.
- Allowlist-only publication boundary and repeatable installer.
- Sanitized automation definitions and empty-vault example.
- `resolve-document-items` skill for closing open questions, unowned actions, assumptions, and pending decisions in documents through a calm one-question-at-a-time workflow.
- DOCX deliverable generation now renders Markdown pipe tables as Word tables.
10 changes: 8 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ framework files with `--force` after reviewing the changes.

## Install the SecondBrain skills

The vault installer keeps canonical skills under `skills/`. To make `$dtm` and
`$voice` discoverable by Codex, copy them into the user's Codex skills
The vault installer keeps canonical skills under `skills/`. To make the
framework skills discoverable by Codex, copy them into the user's Codex skills
directory:

```sh
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
cp -R skills/dtm "${CODEX_HOME:-$HOME/.codex}/skills/"
cp -R skills/voice "${CODEX_HOME:-$HOME/.codex}/skills/"
cp -R skills/markdown-to-pdf "${CODEX_HOME:-$HOME/.codex}/skills/"
cp -R skills/resolve-document-items "${CODEX_HOME:-$HOME/.codex}/skills/"
```

Start a thread in the installed vault and invoke `$dtm`. That thread remains in
Expand All @@ -53,6 +54,11 @@ Invoke `$voice` after representative writing has reached `ready/` or
Invoke `$markdown-to-pdf` when you want a shareable PDF from a Markdown note or
managed document, including Mermaid diagrams when present.

Invoke `$resolve-document-items` when you want to work through open questions,
unowned actions, assumptions, and pending decisions in a document one question
at a time, updating the document only when the answer resolves or records the
item.

## Optional PDF deliverable toolchain

If you want managed Markdown-to-PDF generation with Mermaid support, install:
Expand Down
14 changes: 12 additions & 2 deletions framework/DTM.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ When work is delegated to another thread:

## DTM workspace

- `daily/YYYY-MM-DD.md` — active Daily Notes for the current 14-day retro period.
- `daily/archive/YYYY-MM-DD.md` — archived Daily Notes retained for reference once they age out of the active window.
- `daily/YYYY-MM-DD.md` — Daily Notes retained together in the main daily workspace.
- `projects/` — durable project plans, status, milestones, and next actions.
- `work/` — working documents, drafts, scratch analyses, and deliverables that
are not yet durable wiki knowledge.
Expand Down Expand Up @@ -100,6 +99,8 @@ Working-note statuses are:

Reusable operational assets in `work/`, such as prompt templates, checklists, research packs, or working methods that are still being actively used or revised, should remain `current`. Do not mark a note `reference` merely because it is reusable.

New managed working notes default to `current` while they have an expected next use, even if that use is occasional or advisory. Create a new note directly as `reference` only when it is explicitly a retained historical record, superseded artefact, imported review, or other non-active material, and make the retention reason clear in the note. A newly created working note must not be marked `reference` just because it is a reusable comparison, research pack, or decision-support aid.

Keep project filenames and managed working-note filenames in stable lowercase
kebab-case. Use `templates/project.md` for projects and
`templates/working-note.md` for new managed working notes.
Expand All @@ -114,6 +115,14 @@ physical line and rely on Obsidian for visual wrapping. Use new lines only when
the Markdown structure itself changes, such as headings, list items, tables,
block quotes, or code fences.

## Implementation environment preferences

When advising on, creating, or changing containerised development environments, preserve the user's architecture preference unless the task supplies a stronger local constraint:

- For containers that run custom application code on the user's Mac, prefer `linux/amd64` images under Rosetta, accepting the performance tradeoff in exchange for consistency with the more common non-ARM deployment and runtime architecture.
- For hosted services or off-the-shelf dependency containers where the container is not running custom application code, prefer native ARM images when they are available.
- Deviate from this only for a clear compatibility, tooling, or operational reason, and call out that reason explicitly before proceeding.

## Collaborative writing

Use `templates/writing-piece.md` for substantive pieces. Help develop the
Expand Down Expand Up @@ -218,6 +227,7 @@ traceable at system level.

- Put personal and professional tasks in their respective Daily Note sections.
- Use Obsidian task syntax: `- [ ] Action` and `- [x] Completed action`.
- Treat the Daily Note `Focus` section as the morning commitment for the day, not a live next-action list. After the day has started, do not rewrite `Focus` to reflect progress, newly discovered details, or the next task unless the user explicitly asks to correct the focus wording itself. Track completion and changing operational state in to-do items, `Notes & Activity`, and linked project or working notes.
- Make tasks concrete and outcome-oriented. Link the relevant project when one
exists.
- In the current Daily Note, preserve visible completion by checking completed
Expand Down
93 changes: 93 additions & 0 deletions framework/skills/resolve-document-items/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
name: resolve-document-items
description: Work through unresolved items in a specified document through a structured, non-adversarial question-and-answer workflow. Use when the user invokes `$resolve-document-items` or explicitly asks to close open questions, unowned tasks, incomplete actions, assumptions, or pending decisions in a document one item at a time, updating the source document only when the user's answer resolves, assigns, confirms, changes, or explicitly records deferral for an item.
---

# Resolve Document Items

Use this skill to help close open loops in an existing document calmly and efficiently. The goal is document completion, not idea exploration, red-team review, or Socratic challenge.

## Core Behavior

- Read the specified source document before asking questions.
- Identify unresolved open questions, unowned tasks, incomplete actions, assumptions needing confirmation, stale placeholders, and decisions still required.
- Work through items one at a time.
- Ask exactly one primary question at a time.
- Wait for the user's answer before moving to the next item.
- Ask a brief follow-up only when the answer is ambiguous, incomplete, internally inconsistent, or not enough to update the document accurately.
- Avoid challenging the user's reasoning unless the document cannot be updated safely without clarifying a real contradiction.
- Update the source document as soon as an answer resolves an item, assigns ownership, confirms a decision, changes action status, or records a user-requested deferral.
- Leave the source document unchanged when the user explicitly defers an item without asking for the deferral to be recorded.
- Continue until every identified item is resolved, updated, assigned, or explicitly deferred.

## Distinction From Interview Skills

This skill is not a thought-development interview. Do not use it to test the user's assumptions, deepen their argument, generate new ideas, or pressure-test strategy. Use the minimum question needed to make the document more complete and actionable.

If a user appears to want exploration rather than close-out, ask whether they want this skill or a separate thinking/interview workflow. If they ask to resolve document items, stay practical.

## Workflow

1. Confirm the document path or identify the named document from the current workspace.
2. Read the document and any directly necessary local context, such as a linked index entry or project page that defines statuses or ownership conventions. Do not broaden into unrelated research.
3. Build a short internal queue of outstanding items. Prefer items already visible in headings, tables, checklists, `TBC`, `TODO`, `Open questions`, `Next actions`, `Assumptions`, `Risks`, `Decisions`, and owner/status columns.
4. Start with the item that is most blocking, easiest to resolve, or nearest to the document's current operating purpose. If no order is obvious, use document order.
5. Ask one focused question that makes clear:
- which item is being resolved;
- what answer format would let the document be updated;
- whether the likely update is a decision, owner, action, status, assumption confirmation, or deferral.
6. After the user answers, decide whether the answer is document-ready.
7. If document-ready, update the source document immediately and tell the user briefly what changed.
8. If not document-ready, ask one brief follow-up that names the missing piece.
9. If deferred, leave the source document unchanged unless the user asks to record the deferral.
10. Move to the next item and repeat until the queue is closed or the user stops.

## Question Style

Ask narrow, answerable questions. Prefer:

- "For this open query, should the owner be Philip, Ben, another named person, or left unassigned for now?"
- "Can I mark this as resolved with the decision that the Kanban board remains the live delivery view and the project note is only a periodic snapshot?"
- "Should this action stay open, move to blocked, or be removed because it is no longer required?"
- "Is this assumption now confirmed, or should I leave it as an explicit assumption?"

Avoid:

- "Are you sure?"
- "What are the second-order consequences?"
- "Defend this decision."
- "Let's explore your underlying mental model."
- Multi-part questions that require the user to resolve several items at once.

## Updating The Document

When updating, preserve the document's existing style, structure, and status vocabulary. Make the smallest change that accurately captures the answer.

Common update patterns:

- Replace `TBC` with the confirmed value.
- Add or update an owner in a table.
- Move an action from open to blocked, completed, or deferred if the document has those statuses.
- Remove an open question only when the answer has been integrated into the relevant section.
- Add a concise decision bullet when the document has a decision section.
- Add a short note to an item when the user asks for a deferral or uncertainty to be recorded.

Do not silently rewrite large sections while resolving one item. If an answer implies a broader restructure, say so and ask whether to make that broader edit after the current item is handled.

## Progress Reporting

Keep progress visible but lightweight. After each update, use a short confirmation such as:

`Updated: assigned the workbook review-rhythm action to Philip and left Ben as consulted. Next item: the unresolved safeguarding mapping ownership.`

When the queue is complete, give a concise summary of:

- items resolved;
- items assigned;
- decisions confirmed;
- items explicitly deferred;
- any remaining blockers the document still shows.

## DTM Compatibility

If this skill is invoked inside an active `$dtm` thread, remain within the DTM workflow. Capture substantive document changes in today's Daily Note before closing out. For project, work, writing, or document workspace changes, run the relevant local lint command when one exists.
6 changes: 6 additions & 0 deletions framework/skills/resolve-document-items/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
interface:
display_name: "Resolve Document Items"
short_description: "Close open loops in a document"
default_prompt: "Use $resolve-document-items to work through unresolved items in the specified document one question at a time."
policy:
allow_implicit_invocation: false
94 changes: 94 additions & 0 deletions framework/tools/document_deliverables.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,63 @@ def list_kind(block: str) -> str | None:
return None


def is_markdown_table(block: str) -> bool:
lines = [line.strip() for line in block.splitlines() if line.strip()]
if len(lines) < 2:
return False
if not all(line.startswith("|") and line.endswith("|") for line in lines[:2]):
return False
separator_cells = [cell.strip() for cell in lines[1].strip("|").split("|")]
return bool(separator_cells) and all(re.match(r"^:?-{3,}:?$", cell) for cell in separator_cells)


def parse_markdown_table(block: str) -> tuple[list[str], list[list[str]]]:
lines = [line.strip() for line in block.splitlines() if line.strip()]
rows = [[cell.strip() for cell in line.strip("|").split("|")] for line in lines]
header = rows[0]
body = rows[2:]
width = len(header)
normalized = [(row + [""] * width)[:width] for row in body]
return header, normalized


def set_cell_margins(cell, *, top: int = 80, start: int = 80, bottom: int = 80, end: int = 80) -> None:
tc_pr = cell._tc.get_or_add_tcPr()
tc_mar = tc_pr.first_child_found_in("w:tcMar")
if tc_mar is None:
tc_mar = OxmlElement("w:tcMar")
tc_pr.append(tc_mar)
for name, value in (("top", top), ("start", start), ("bottom", bottom), ("end", end)):
element = tc_mar.find(qn(f"w:{name}"))
if element is None:
element = OxmlElement(f"w:{name}")
tc_mar.append(element)
element.set(qn("w:w"), str(value))
element.set(qn("w:type"), "dxa")


def shade_cell(cell, fill: str) -> None:
tc_pr = cell._tc.get_or_add_tcPr()
shd = tc_pr.find(qn("w:shd"))
if shd is None:
shd = OxmlElement("w:shd")
tc_pr.append(shd)
shd.set(qn("w:fill"), fill)


def markdown_table_widths(headers: list[str], rows: list[list[str]]) -> list[float]:
lengths: list[int] = []
for idx, header in enumerate(headers):
body_lengths = [len(row[idx]) for row in rows if idx < len(row)]
lengths.append(max([len(header), *body_lengths, 4]))
total = sum(lengths) or 1
raw = [CONTENT_WIDTH_IN * (length / total) for length in lengths]
minimum = 0.65 if len(headers) >= 5 else 0.9
adjusted = [max(minimum, width) for width in raw]
scale = CONTENT_WIDTH_IN / sum(adjusted)
return [width * scale for width in adjusted]


def set_font(run, name: str, size: float, *, bold: bool = False, italic: bool = False, color: str = "000000") -> None:
run.font.name = name
run._element.rPr.rFonts.set(qn("w:ascii"), name)
Expand Down Expand Up @@ -464,6 +521,40 @@ def add_list(doc: Document, block: str, ordered: bool) -> None:
set_font(run, "Calibri", 11, color="000000")


def add_markdown_table(doc: Document, block: str) -> None:
headers, rows = parse_markdown_table(block)
if not headers:
return

table = doc.add_table(rows=1, cols=len(headers))
table.style = "Table Grid"
table.autofit = False
widths = markdown_table_widths(headers, rows)
font_size = 8 if len(headers) >= 5 else 9

for idx, (cell, header) in enumerate(zip(table.rows[0].cells, headers, strict=True)):
cell.width = Inches(widths[idx])
set_cell_margins(cell, top=100, start=100, bottom=100, end=100)
shade_cell(cell, "D9EAF7")
para = cell.paragraphs[0]
style_paragraph(para, after=0, line=240)
run = para.add_run(clean_inline(header))
set_font(run, "Calibri", font_size, bold=True, color="1F1F1F")

for row_values in rows:
row = table.add_row()
for idx, cell in enumerate(row.cells):
cell.width = Inches(widths[idx])
set_cell_margins(cell, top=90, start=100, bottom=90, end=100)
para = cell.paragraphs[0]
style_paragraph(para, after=0, line=240)
run = para.add_run(clean_inline(row_values[idx]))
set_font(run, "Calibri", font_size, color="000000")

spacer = doc.add_paragraph()
style_paragraph(spacer, after=8, line=240)


def add_summary_table(doc: Document, title: str, summary: str, recommendation: str) -> None:
table = doc.add_table(rows=3, cols=2)
table.style = "Table Grid"
Expand Down Expand Up @@ -522,6 +613,9 @@ def build_docx(source: Path, output: Path) -> Path:
if section.title and lowered != "document":
add_heading(doc, section.title, section.level)
for block in section.blocks:
if is_markdown_table(block):
add_markdown_table(doc, block)
continue
kind = list_kind(block)
if kind == "bullet":
add_list(doc, block, ordered=False)
Expand Down
Loading
Loading