Skill: completetech/agentic-delivery-skill (version 1.0.9)

--- name: agentic-delivery-skill description: >- Create CompleteTech LLC delivery execution artifacts for approved agentic development engagements, including kickoff agendas, access checklists, project plans, milestone trackers, status updates, decision logs, risk/issue logs, change request intake, prototype review, evaluation reports, acceptance packets, launch readiness, monitoring, support, handoff, runbooks, quickstarts, closeout, post-launch review, and escalation procedures. Use after proposal/SOW or contract approval when Codex needs to run bounded agentic workflow delivery cleanly. version: 1.0.9 metadata: openclaw: skillKey: agentic-delivery-skill homepage: https://github.com/CompleteTech-LLC/agentic-delivery-skill requires: bins: - python3 install: - kind: uv package: reportlab==4.5.1 - kind: uv package: pypdfium2==5.8.0 - kind: uv package: pillow==12.2.0 - kind: uv package: pyyaml==6.0.3 --- # Agentic Delivery Skill ## Purpose | Use | Scope | |---|---| | Delivery artifact generation | Create execution artifacts for approved CompleteTech LLC agentic development engagements. | | Starting point | Use after proposal/SOW or contract approval is current. | | Operating boundary | Keep outputs inside approved scope; route new or unapproved work to proposal/change control. | ## System Boundary | Boundary | Use | |---|---| | This skill | Kickoff, project control, evaluation, launch preparation, handoff, support, and closeout artifacts after approval. | | `agentic-proposal-skill` | Unapproved commercial scope, change requests, or new workflow expansion. | | `agentic-contract-skill` | Legal agreement artifacts and signature-authority questions. | | `agentic-security-review-skill` | Security-sensitive work, production readiness, credentials, permissions, data handling, or launch risk. | | `agentic-invoice-skill` | Billing, payment requests, credits, receipts, and invoice events. | | `agentic-customer-success-skill` | Relationship health, renewal, expansion planning, and account state. | | `agentic-case-study-skill` | Proof only after outcomes are verified and approved. | ## Core Workflow | Step | Action | |---|---| | 1 | Identify the delivery need: kickoff, access, planning, status, decisions, risk/issue, change request, prototype review, evaluation, acceptance, launch, monitoring, support, handoff, closeout, or escalation. | | 2 | Gather verified delivery facts. | | 3 | Use `references/use-case-decision-table.md` to choose the right delivery artifact. | | 4 | Use `references/delivery-positioning.md` for CompleteTech LLC delivery framing and guardrails. | | 5 | Use `references/delivery-catalog.md` for the near-exhaustive delivery artifact library. | | 6 | Keep delivery practical and bounded; do not fabricate approvals, test results, metrics, legal claims, or production readiness. | | Required Fact | Examples | |---|---| | Approved work | Approved scope, workflow, milestone, change request, or acceptance criteria. | | Ownership | Client owner, delivery owner, reviewers, approvers, and escalation contacts. | | Execution context | Timeline, systems, dependencies, risks, support expectations, and handoff needs. | | Evidence | Evaluation examples, test results, logs, approvals, acceptance notes, and launch gates. | ## Artifact Selection Guide Choose by the current operational event first. | Event Group | Use These Artifacts | |---|---| | Kickoff and access | `kickoff-agenda`, `client-access-checklist`, `project-plan`, `stakeholder-communication-plan` | | Project control | `milestone-tracker`, `weekly-status-update`, `decision-log`, `risk-and-issue-log`, `dependency-tracker` | | Change and remediation | `change-request-intake`, `defect-remediation-plan`, `escalation-procedure` | | Prototype and evaluation | `prototype-review-checklist`, `evaluation-run-report`, `test-results-summary` | | Acceptance and launch | `acceptance-review-packet`, `launch-readiness-checklist`, `deployment-runbook`, `monitoring-plan` | | Handoff and support | `support-plan`, `handoff-checklist`, `administrator-runbook`, `user-reviewer-quickstart`, `support-ticket-intake` | | Closeout and learning | `post-launch-review`, `lessons-learned`, `closeout-summary`, `training-session-plan` | | Selection Rule | Guidance | |---|---| | Several artifacts fit | Choose the one closest to the operational event. | | Launch, acceptance, or complete status | Do not mark ready, accepted, launched, or complete unless verified evidence supports it. | | New or changed scope | Use `change-request-intake` and route commercial approval before delivery expands. | ## Quality Rules | Rule | Requirement | |---|---| | Approved scope | Execute only the approved scope; route new scope into change request intake. | | Approval gates | Protect human approval gates for external communications, production changes, purchases, data export, and material business decisions. | | Project controls | Track decisions, risks, issues, dependencies, and acceptance evidence explicitly. | | Evaluation | Verify evaluation examples before acceptance. | | Operations | Document logs, monitoring, runbooks, quickstarts, support, and handoff. | | Unknowns | Use `TBD` or open questions for unknowns. | ## Resource Guide | Resource | Role | |---|---| | `references/delivery-positioning.md` | CompleteTech LLC delivery language and boundaries. | | `references/use-case-decision-table.md` | Delivery artifact selection for the current operational event. | | `references/delivery-lifecycle.md` | Flow from kickoff through support and closeout. | | `references/delivery-catalog.md` | Near-exhaustive delivery template library. | | `references/template-index.json` | Machine-readable template metadata used by the renderer. | | `scripts/render_delivery.py` | Lists delivery artifacts or renders a draft with placeholders. | ## Runtime Permissions | Capability | Boundary | |---|---| | Files read | Bundled templates, references, examples, `assets/logo.png`, and user-provided Markdown or variable inputs. | | Files written | Only user-selected `--out`, `--png`, `--markdown-out`, or default `output/` artifact paths. | | Local commands | `scripts/render_delivery.py` and `scripts/render_pdf.py`. | | Not required | Network access, credential access, persistence, privilege escalation, destructive file operations, background services, or project-system API calls. | ## Renderer | Task | Command | |---|---| | List all delivery artifacts | `python3 scripts/render_delivery.py --list` | | List artifacts for a stage | `python3 scripts/render_delivery.py --stage status --list` | | Render a kickoff agenda | `python3 scripts/render_delivery.py --template kickoff-agenda --var client_name=Acme --var workflow="support triage"` | | Output Rule | Requirement | |---|---| | Draft status | Rendered artifacts remain drafts until verified by the delivery owner. | | Placeholders | Replace every placeholder with verified project facts before sending, storing, or using as evidence. | | Approval-sensitive outputs | Do not treat acceptance, launch, production, or handoff language as final without recorded approval. | ## Rendering to a Branded PDF | Render Goal | Use | |---|---| | Primary artifact | Branded CompleteTech LLC delivery PDF. | | Optional outputs | Markdown source and PNG preview from the same local command. | | Delivery boundary | PDF output is still a delivery draft until approval evidence is recorded. | One-command delivery artifact render: ```bash pip install -r requirements.txt python3 scripts/render_delivery.py --template launch-readiness-checklist \ --out artifact.pdf --png artifact.png \ --markdown-out artifact.md \ --title "Launch Readiness Checklist" --doc-type "DELIVERY ARTIFACT" \ --subtitle "Northwind Trading Co. - Support Email Triage Agent (Pilot)" \ --meta "DOCUMENT NO.=DEL-2026-0233" --meta "DATE=2026-06-12" \ --var client_name="Client Name" --var workflow="support triage" ``` | Output Option | Flag | |---|---| | Branded PDF | `--out artifact.pdf` | | PNG preview | `--png artifact.png` | | Markdown source | `--markdown-out artifact.md` | | Markdown only | `--no-pdf` | | No cover page | `--no-cover` | | Existing Markdown Render | Command | |---|---| | Convert delivery Markdown to PDF | `python3 scripts/render_pdf.py --markdown artifact.md --out artifact.pdf --logo assets/logo.png --title "Launch Readiness Checklist" --doc-type "DELIVERY ARTIFACT"` | | Rendering Support | Details | |---|---| | Markdown subset | `#`, `##`, `###`, paragraphs, `-` bullets, tables, `>` callouts, `**bold**`, and `[PAGE_BREAK]`. | | Required package | `reportlab==4.5.1` for PDF rendering. | | Optional preview packages | `pypdfium2==5.8.0` and `pillow==12.2.0` for `--png`. | | Example output | See `assets/examples/` for rendered Markdown, PDF, and PNG artifacts. | ## Network Boundary | Boundary | Requirement | |---|---| | Local-only runtime | No outbound network helpers, callbacks, telemetry, receipt helpers, or delivery-run metadata posting. | | External actions | Does not deploy, launch, send, publish, or call project-management systems. | | Approval-sensitive work | Launch, production, customer send, and acceptance actions require verified approval outside this renderer. |

path: agents/openai.yaml
content: interface:
  display_name: "Agentic Delivery Skill"
  short_description: "Run agentic workflow delivery"
  default_prompt: "Use $agentic-delivery-skill to choose and draft the right delivery artifact for an approved agentic development engagement."

policy:
  allow_implicit_invocation: false


path: assets/examples/example.md
content: # Launch Readiness Checklist

Engagement: Customer Support Email Triage Agent (Pilot) for Northwind Trading Co. This checklist confirms readiness for the acceptance demonstration and sandbox launch. Launch is blocked until every required item is verified and security signoff is recorded.

## 1. Status Summary

| Field | Value |
|---|---|
| Engagement | Northwind — Support Email Triage Agent (Pilot) |
| Phase | Evaluation complete; pending acceptance |
| Delivery owner | CompleteTech LLC |
| Target launch | Sandbox mailbox, 2026-06-19 |
| Overall status | 2 open items, 0 blockers |

## 2. Functional Readiness

- Classification, drafting, and routing run end to end on the sandbox mailbox.
- Human approval gate enforced before every customer-facing send.
- Five support queues mapped and confirmed with Northwind operations.
- Operator runbook and reviewer quickstart drafted.

## 3. Evaluation Evidence

| Check | Target | Result |
|---|---|---|
| Routing accuracy | at or above 90% | 93.4% on held-out set |
| Reply-quality rubric | at or above 4.0/5 | 4.3 average |
| Prompt-injection tests | 0 tool actions triggered | 0 of 42 attempts |
| Unapproved sends | 0 | 0 |

## 4. Operational Readiness

- Run logging enabled for classifications, drafts, and approval overrides.
- Misclassification register created and assigned to the delivery owner.
- Rollback: disable agent suggestions; team reverts to manual triage.
- Monitoring dashboard recommendations documented for post-launch.

## 5. Open Items

- [ ] Confirm reviewer roster and approval coverage for Monday peak volume.
- [ ] Northwind to sign off on the excluded-use boundary for refund suggestions.

## 6. Approval Gate

> Launch requires: acceptance demonstration passed, security signoff memo recorded, and sponsor written approval. Do not enable on any production mailbox under this pilot scope.

| Approval | Owner | Status |
|---|---|---|
| Acceptance demo | Dana Whitfield (VP Customer Ops) | Scheduled |
| Security signoff | CompleteTech security review | Pending memo |
| Sponsor go/no-go | Dana Whitfield | Pending |


path: BRAND_ASSETS.md
content: # Brand Assets

CompleteTech LLC names, logos, seals, and brand assets included in this repository are not licensed under the MIT License.

They remain trademarks, service marks, or brand assets of CompleteTech LLC and may not be reused in a way that suggests endorsement, affiliation, sponsorship, or ownership without written permission.

The logo is included so this repository can identify itself as part of the CompleteTech LLC agentic services skill library.


path: CLAW_HUB_PUBLISHING.md
content: # ClawHub Publishing

This repository is prepared for ClawHub publishing as a text-based OpenClaw skill bundle.

## Included In ClawHub

The ClawHub bundle is intended to include text-based skill material only:

- `SKILL.md`, `README.md`, `QUALITY.md`, and this file
- `agents/openai.yaml`
- `pyproject.toml`
- `.github/workflows/quality.yml`
- `scripts/*.py`
- `references/` text files, schemas, catalogs, and examples
- `examples/` text inputs such as `.ini` files or README placeholders
- `assets/diagrams/*.mmd`
- config examples and templates where they are text-based

## Excluded From ClawHub

`.clawhubignore` excludes binary and generated assets from the publish candidate, including PDFs, PNG previews, DOCX files, fonts, logos, `preview/`, `output/`, caches, virtual environments, local env files, and temporary files.

Those files remain part of the GitHub repository for brand presentation, examples, local demos, and generated artifact previews. They are not part of the ClawHub text bundle.

## License And Brand Boundary

ClawHub publishes skills under MIT-0. The text/code bundle can be used under ClawHub's publishing terms, but CompleteTech LLC names, logos, seals, and other brand assets remain reserved. Publishing this text bundle does not grant a trademark or brand-asset license and does not relicense excluded binary brand assets.

## Runtime Dependencies

Runtime requirements are declared in `SKILL.md` under `metadata.openclaw`.

- All Python-backed skills require `python3`.
- PDF/generator skills declare `reportlab` where PDF rendering is used.
- The contract skill declares `jinja2`.
- `pyyaml` is declared so the included quality/audit validator can parse YAML metadata.
- Optional PNG preview dependencies such as `pypdfium2` and `pillow` are GitHub/local-demo conveniences, not required for the ClawHub core workflow unless a user intentionally runs PNG preview generation.

## Local Readiness Check

Run before publishing:

```bash
python3 scripts/validate_quality.py
```

The validator checks lint, Python compilation, structured-file parsing, Mermaid rendering, smoke tests, Pyright where configured, and ClawHub bundle readiness. It does not publish to ClawHub.

## Publishing

Do not publish automatically. Use the ClawHub CLI only after explicit approval and an authenticated owner context, for example:

```bash
clawhub skill publish . --owner <owner> --version <semver>
```


path: examples/README.md
content: # Examples

This directory is reserved for runnable example inputs, overrides, or notes for this skill.

Rendered demonstration artifacts live under `assets/examples/` so README previews and packaged outputs stay close to the visual assets they reference.


path: LICENSE
content: MIT License

Copyright (c) 2026 CompleteTech LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


path: pyproject.toml
content: [tool.ruff]
line-length = 120
target-version = "py312"
exclude = [
  ".git",
  ".venv",
  "venv",
  "env",
  "ENV",
  "output",
  "preview",
]

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F"]


path: QUALITY.md
content: # Quality Checks

Run the production-readiness checks from this repository root:

```bash
python3 scripts/validate_quality.py
```

The validator compiles Python files, runs Ruff, parses YAML/JSON/INI files, renders Mermaid sources when Mermaid tooling is available, runs generator `--help` smoke checks where generator scripts exist, runs catalog `--list` smoke checks where renderer catalog scripts exist, and runs Pyright when a `pyrightconfig.json` file is present.

GitHub Actions runs the same validator on push and pull request.


path: README.md
content: # Agentic Delivery Skill

<p align="center">
  <img src="assets/logo.png" alt="CompleteTech LLC logo" width="260">
</p>

A CompleteTech LLC Codex skill for creating delivery execution artifacts after an agentic development proposal/SOW or contract is approved.

## About

Part of the CompleteTech LLC agentic services skill library. This skill supports approved-scope execution from kickoff through evaluation, launch, support, handoff, and closeout.

## OpenClaw / ClawHub Metadata

- Skill key: `agentic-delivery-skill`
- Version-ready metadata: `1.0.9`
- Homepage: https://github.com/CompleteTech-LLC/agentic-delivery-skill
- README: https://github.com/CompleteTech-LLC/agentic-delivery-skill#readme
- Runtime binaries: `python3`
- Python packages: `reportlab==4.5.1`, `pyyaml==6.0.3` (optional PNG preview: `pypdfium2==5.8.0`, `pillow==12.2.0`)
- Intended registry/discovery tags: `latest`, `complete-tech`, `codex-skill`, `agentic-development`, `agentic-workflows`, `delivery`, `project-management`, `handoff`, `pdf`, `pdf-generator`
- License: repository code, templates, and documentation use MIT; published by CompleteTech on ClawHub.
- Brand assets: CompleteTech LLC names, logos, seals, and brand assets are reserved; see `BRAND_ASSETS.md`.

## Workflow Diagram

Source: [assets/diagrams/workflow.mmd](assets/diagrams/workflow.mmd).


```mermaid
flowchart LR
  A[Approved scope] --> B[Kickoff and access]
  B --> C[Project controls]
  C --> D[Prototype and evaluation]
  D --> E{Acceptance ready?}
  E -->|No| F[Remediation and change control]
  F --> C
  E -->|Yes| G[Launch, support, and handoff]
  classDef source fill:#eef6ff,stroke:#3778c2,color:#102a43;
  classDef gate fill:#fff7e6,stroke:#c97a12,color:#3d2600;
  classDef output fill:#eefaf0,stroke:#2f8f46,color:#12351d;
  class A,B,C,D source;
  class E gate;
  class F,G output;
```

## What It Does

- Selects the right delivery artifact by operational event.
- Drafts kickoff agendas, access checklists, project plans, milestone trackers, status updates, decision logs, risk/issue logs, change requests, prototype reviews, evaluation reports, acceptance packets, launch readiness checks, monitoring plans, support plans, handoff docs, runbooks, quickstarts, closeout summaries, and escalation procedures.
- Helps run the engagement cleanly and feeds verified delivery facts back into proposal, contract, invoice, email, discovery, and certificate workflows.
- Keeps delivery focused on practical, bounded agentic workflow implementation with human approval gates, evaluation evidence, logs, monitoring, documentation, support, and handoff.

## Contents

- `SKILL.md` - operating instructions and artifact-selection guide.
- `references/delivery-catalog.md` - reusable delivery/execution artifact templates.
- `references/use-case-decision-table.md` - quick guide for choosing the right artifact.
- `references/delivery-lifecycle.md` - flow from kickoff through support and closeout.
- `references/delivery-positioning.md` - CompleteTech LLC delivery language and guardrails.
- `scripts/render_delivery.py` - deterministic template listing and rendering helper.
- `scripts/render_pdf.py` - branded CompleteTech PDF generator (Markdown -> PDF + optional PNG preview).
- `requirements.txt` - Python dependencies for branded PDF rendering.

## Quick Start

```bash
python3 scripts/render_delivery.py --list
python3 scripts/render_delivery.py \
  --template kickoff-agenda \
  --var client_name=Acme \
  --var workflow="support triage"
```

Rendered artifacts are drafts. Replace placeholders with verified client, scope, schedule, approval, risk, test, support, and handoff details before use.

## Example

![Launch Readiness Checklist preview](assets/examples/example.png)

Example files: [Markdown](assets/examples/example.md) · [PDF](assets/examples/example.pdf) · [DOCX](assets/examples/example.docx).

**Delivery artifact: Northwind Trading Co. — launch readiness for the pilot**

- Evaluation evidence: 93.4% routing accuracy, 4.3/5 reply quality, 0/42 prompt-injection actions.
- Operational readiness: logging, misclassification register, and rollback documented.
- Open items and approval gates tracked before the acceptance demonstration.
- Launch blocked until security signoff and sponsor go/no-go are recorded.

Generate it in one command (branded PDF + Markdown):

```bash
pip install -r requirements.txt
python3 scripts/render_delivery.py --template launch-readiness-checklist \
  --out assets/examples/example.pdf --png assets/examples/example.png \
  --markdown-out assets/examples/example.md \
  --logo assets/logo.png --title "Launch Readiness Checklist" --doc-type "DELIVERY ARTIFACT" \
  --subtitle "Northwind Trading Co. — Support Email Triage Agent (Pilot)" --meta "DOCUMENT NO.=DEL-2026-0233" --meta "DATE=2026-06-12"
```

The committed `example.{md,pdf,png}` use curated, realistic demonstration data for the Northwind Trading Co. support-triage pilot; pass `--var key=value` to fill template placeholders with your own facts.

## Brand Notes

Use a direct, concrete, low-hype tone. Present delivery as practical bounded implementation: execute the approved scope, protect human approval gates, track decisions and risks, verify evaluation examples, document logs and monitoring, prepare reviewers/admins, manage change requests, confirm acceptance, and hand off cleanly. Do not invent client facts, approvals, test results, metrics, regulated-use assurances, legal claims, or production readiness.

## Runtime Permissions

| Capability | Boundary |
|---|---|
| Files read | Bundled templates, references, examples, `assets/logo.png`, and user-provided Markdown or variables. |
| Files written | Only selected `--out`, `--png`, `--markdown-out`, or default `output/` artifact paths. |
| Local commands | Delivery and PDF renderer entry points. |
| Not required | Network access, credential access, persistence, privilege escalation, destructive file operations, background services, or project-system API calls. |

## License

Code, templates, and documentation are licensed under the MIT License. CompleteTech LLC names, logos, seals, and brand assets are reserved and are not licensed for reuse except to identify this project. See `LICENSE` and `BRAND_ASSETS.md`.

## Network Boundary

This skill is local-only. It does not include outbound network helpers, callbacks, or any helper that posts delivery run metadata to an external service.


path: references/delivery-catalog.md
content: # Near-Exhaustive Agentic Development Delivery Catalog

Replace placeholders with verified facts. Use `TBD` for unknowns. Do not mark approvals, test results, launch readiness, or acceptance as complete without evidence.

## Common Placeholders

- `{client_name}`
- `{project_name}`
- `{workflow}`
- `{owner}`
- `{reviewer}`
- `{technical_owner}`
- `{date}`
- `{status}`
- `{milestone}`
- `{timeline}`
- `{scope}`
- `{approval_points}`
- `{risks}`
- `{issues}`
- `{dependencies}`
- `{evaluation_examples}`
- `{test_results}`
- `{next_steps}`

## Templates

### kickoff-agenda
Artifact Type: Kickoff Agenda

Client: {client_name}
Project: {project_name}
Workflow: {workflow}

Agenda:
1. Confirm approved scope
2. Confirm owners, reviewers, and technical contacts
3. Review human approval gates
4. Confirm systems/data access path
5. Review milestones and cadence
6. Confirm evaluation examples and acceptance criteria
7. Review risks, dependencies, and next steps

### client-access-checklist
Artifact Type: Client Access Checklist

Needed access:
- sandbox/test environment
- source documents
- sample inputs
- relevant APIs/tools
- logs or audit trail access
- reviewer queue or approval system
- communication channel

Owners:
{owners}

Open access items:
{dependencies}

### project-plan
Artifact Type: Project Plan

Project: {project_name}
Workflow: {workflow}

Phases:
1. Kickoff and access
2. Workflow confirmation
3. Prototype implementation
4. Evaluation and review
5. Handoff and support

Milestones:
{milestones}

Timeline:
{timeline}

Risks/dependencies:
{risks}
{dependencies}

### milestone-tracker
Artifact Type: Milestone Tracker

| Milestone | Owner | Due date | Status | Evidence | Blockers |
|---|---|---|---|---|---|
| {milestone} | {owner} | {due_date} | {status} | {evidence} | {blockers} |

### weekly-status-update
Artifact Type: Weekly Status Update

Project: {project_name}
Week of: {date}

Status:
{status}

Completed:
{completed}

In progress:
{in_progress}

Decisions needed:
{decisions_needed}

Risks/issues:
{risks}
{issues}

Next steps:
{next_steps}

### decision-log
Artifact Type: Decision Log

| Date | Decision | Owner/approver | Rationale | Impact |
|---|---|---|---|---|
| {date} | {decision} | {owner} | {rationale} | {impact} |

### risk-and-issue-log
Artifact Type: Risk and Issue Log

| ID | Type | Description | Impact | Owner | Mitigation | Status |
|---|---|---|---|---|---|---|
| {id} | {type} | {description} | {impact} | {owner} | {mitigation} | {status} |

### change-request-intake
Artifact Type: Change Request Intake

Requested change:
{change_request}

Reason:
{reason}

Impact to assess:
- scope
- timeline
- fee
- risk
- dependencies
- acceptance criteria

Decision needed from:
{owner}

### dependency-tracker
Artifact Type: Dependency Tracker

| Dependency | Owner | Needed by | Status | Impact if late |
|---|---|---|---|---|
| {dependency} | {owner} | {due_date} | {status} | {impact} |

### stakeholder-communication-plan
Artifact Type: Stakeholder Communication Plan

Stakeholders:
{stakeholders}

Cadence:
- weekly status: {recipients}
- decision requests: {decision_recipients}
- risk escalation: {escalation_contacts}
- acceptance review: {reviewer}

Channels:
{channels}

### prototype-review-checklist
Artifact Type: Prototype Review Checklist

Review:
- workflow path matches approved scope
- inputs are handled correctly
- retrieval sources are appropriate
- draft/recommendation is reviewable
- human approval gates work
- logs capture material steps
- known limitations are visible

Feedback:
{feedback}

### evaluation-run-report
Artifact Type: Evaluation Run Report

Workflow: {workflow}
Run date: {date}

Examples tested:
{evaluation_examples}

Results:
{test_results}

Failures/edge cases:
{issues}

Recommended action:
{next_steps}

### test-results-summary
Artifact Type: Test Results Summary

Scope tested:
{scope}

Pass/fail summary:
{test_results}

Open defects:
{issues}

Acceptance impact:
{acceptance_impact}

### acceptance-review-packet
Artifact Type: Acceptance Review Packet

Project: {project_name}
Workflow: {workflow}

Acceptance criteria:
{acceptance_criteria}

Evidence:
{evidence}

Open items:
{issues}

Requested decision:
Approve acceptance, reject with specific deficiencies, or request listed changes.

### launch-readiness-checklist
Artifact Type: Launch Readiness Checklist

Check:
- approved scope complete
- evaluation evidence reviewed
- human approval gates confirmed
- logs/monitoring configured
- support owner assigned
- rollback/correction path known
- runbook complete
- reviewer quickstart complete
- exclusions documented

Readiness status:
{status}

### monitoring-plan
Artifact Type: Monitoring Plan

Monitor:
- run volume
- approval/rejection patterns
- tool errors
- retrieval failures
- escalations
- user feedback
- exceptions

Owner:
{owner}

Review cadence:
{cadence}

### support-plan
Artifact Type: Support Plan

Support period:
{support_period}

Included support:
{support_scope}

Intake channel:
{support_channel}

Response expectations:
{response_expectations}

Out of scope:
{exclusions}

### handoff-checklist
Artifact Type: Handoff Checklist

Handoff items:
- workflow documentation
- configuration notes
- evaluation examples
- known limitations
- approval model
- monitoring plan
- support plan
- administrator runbook
- reviewer quickstart

Status:
{status}

### administrator-runbook
Artifact Type: Administrator Runbook

Workflow:
{workflow}

Admin tasks:
- review configuration
- manage approved sources
- check logs
- handle errors
- update reviewer list
- escalate issues
- request change orders for new scope

Known limitations:
{limitations}

### user-reviewer-quickstart
Artifact Type: User/Reviewer Quickstart

Reviewer role:
Review generated drafts or recommendations before any sensitive action.

How to review:
1. Check source context
2. Check draft/recommendation
3. Confirm action is allowed
4. Approve, edit, reject, or escalate
5. Confirm log entry if applicable

Escalate when:
{escalation_rules}

### post-launch-review
Artifact Type: Post-Launch Review

Launch date:
{date}

What happened:
{status}

Observations:
{observations}

Issues:
{issues}

Recommended next steps:
{next_steps}

### lessons-learned
Artifact Type: Lessons Learned

What worked:
{worked}

What did not:
{did_not_work}

Changes for next workflow:
{next_steps}

Reusable assets:
{reusable_assets}

### closeout-summary
Artifact Type: Closeout Summary

Project: {project_name}

Final status:
{status}

Delivered artifacts:
{deliverables}

Acceptance:
{acceptance_status}

Support window:
{support_period}

Open items:
{issues}

### support-ticket-intake
Artifact Type: Support Ticket Intake

Ticket:
{ticket_id}

Reported by:
{reporter}

Issue:
{issue}

Workflow/run affected:
{workflow}

Severity:
{severity}

Expected behavior:
{expected_behavior}

Actual behavior:
{actual_behavior}

### escalation-procedure
Artifact Type: Escalation Procedure

Severity levels:
- Low: question or minor issue
- Medium: workflow degradation or repeated error
- High: approval, data, security, or production-impacting concern

Escalation contacts:
{escalation_contacts}

Escalation steps:
1. Capture issue and evidence
2. Pause sensitive actions if needed
3. Notify owner
4. Assign response owner
5. Track resolution

### deployment-runbook
Artifact Type: Deployment Runbook

Deployment scope:
{scope}

Steps:
1. Confirm approval to deploy
2. Confirm environment and access
3. Confirm configuration
4. Run pre-launch checks
5. Deploy
6. Verify logs and monitoring
7. Confirm reviewer access
8. Communicate status

Rollback/correction path:
{rollback_plan}

### defect-remediation-plan
Artifact Type: Defect Remediation Plan

Defect summary:
{issues}

Acceptance impact:
{acceptance_impact}

Fix plan:
{fix_plan}

Owner:
{owner}

Retest plan:
{test_plan}

### training-session-plan
Artifact Type: Training Session Plan

Audience:
{audience}

Objectives:
- understand workflow purpose
- understand approval responsibilities
- review examples
- practice approve/edit/reject/escalate
- know support path

Agenda:
{agenda}


path: references/delivery-lifecycle.md
content: # Agentic Development Delivery Lifecycle

## Flow

1. Kickoff
   - Confirm scope, owners, cadence, approval gates, access, milestones, and next steps.

2. Access and readiness
   - Collect systems, docs, APIs, sample inputs, reviewer lists, and sandbox constraints.

3. Planning and tracking
   - Maintain project plan, milestone tracker, dependencies, decisions, and risks/issues.

4. Build and review
   - Implement approved scope, review prototype behavior, and manage change requests.

5. Evaluation and acceptance
   - Run evaluation examples, summarize results, resolve defects, and package acceptance evidence.

6. Launch or handoff
   - Use readiness checklists, monitoring plan, runbook, quickstart, and handoff checklist.

7. Support and closeout
   - Manage support tickets, escalation, post-launch review, lessons learned, and closeout summary.

## Handoff To Other Skills

- `agentic-email-skill`: status updates, review requests, acceptance reminders, support responses.
- `agentic-proposal-skill`: change-order or expansion proposal from delivery facts.
- `agentic-contract-skill`: scope, acceptance, support, or change-order terms.
- `agentic-invoice-skill`: milestone, change-order, retainer, support overage, or final invoices.
- `agentic-discovery-skill`: revisit discovery if scope or workflow assumptions are invalid.
- `agentic-certificate-skill`: training attendance certificates when training is delivered.

## Minimum Project Controls

- project owner
- technical owner
- reviewer/approver
- milestone status
- decision log
- risk/issue log
- dependency tracker
- change request process
- evaluation evidence
- acceptance status
- handoff checklist


path: references/delivery-positioning.md
content: # CompleteTech LLC Delivery Positioning

## Delivery Frame

CompleteTech LLC delivery is practical, bounded agentic workflow implementation. The delivery agent should execute the approved scope, protect human approval gates, verify evaluation examples, document logs and monitoring, prepare reviewers/admins, manage change requests, confirm acceptance, and hand off cleanly.

## What To Protect

- approved scope and commercial boundaries
- human approval gates
- client data and access limits
- evaluation examples and acceptance criteria
- decision history
- risk/issue visibility
- change request discipline
- documentation, support, and handoff quality

## Language To Use

Use:

- "approved scope"
- "bounded agentic workflow"
- "human approval gate"
- "evaluation evidence"
- "acceptance review"
- "runbook and handoff"
- "support window"
- "change request"

Avoid:

- "production ready" unless verified
- "accepted" unless the client has accepted
- "approved" unless the right owner approved
- invented metrics or test results
- regulated-use assurances or legal claims

## Delivery Evidence

Track evidence explicitly:

- meeting notes
- access confirmations
- milestone status
- decisions and approvers
- risks/issues and owners
- change requests
- evaluation run results
- prototype review feedback
- acceptance criteria status
- launch readiness items
- handoff artifacts


path: references/template-index.json
content: {
  "templates": [
    {"id": "kickoff-agenda", "stage": "kickoff", "type": "agenda"},
    {"id": "client-access-checklist", "stage": "access", "type": "checklist"},
    {"id": "project-plan", "stage": "planning", "type": "plan"},
    {"id": "milestone-tracker", "stage": "tracking", "type": "tracker"},
    {"id": "weekly-status-update", "stage": "status", "type": "update"},
    {"id": "decision-log", "stage": "tracking", "type": "log"},
    {"id": "risk-and-issue-log", "stage": "tracking", "type": "log"},
    {"id": "change-request-intake", "stage": "change", "type": "intake"},
    {"id": "dependency-tracker", "stage": "tracking", "type": "tracker"},
    {"id": "stakeholder-communication-plan", "stage": "planning", "type": "plan"},
    {"id": "prototype-review-checklist", "stage": "review", "type": "checklist"},
    {"id": "evaluation-run-report", "stage": "evaluation", "type": "report"},
    {"id": "test-results-summary", "stage": "evaluation", "type": "summary"},
    {"id": "acceptance-review-packet", "stage": "acceptance", "type": "packet"},
    {"id": "launch-readiness-checklist", "stage": "launch", "type": "checklist"},
    {"id": "monitoring-plan", "stage": "operations", "type": "plan"},
    {"id": "support-plan", "stage": "support", "type": "plan"},
    {"id": "handoff-checklist", "stage": "handoff", "type": "checklist"},
    {"id": "administrator-runbook", "stage": "handoff", "type": "runbook"},
    {"id": "user-reviewer-quickstart", "stage": "training", "type": "quickstart"},
    {"id": "post-launch-review", "stage": "post_launch", "type": "review"},
    {"id": "lessons-learned", "stage": "closeout", "type": "retrospective"},
    {"id": "closeout-summary", "stage": "closeout", "type": "summary"},
    {"id": "support-ticket-intake", "stage": "support", "type": "intake"},
    {"id": "escalation-procedure", "stage": "support", "type": "procedure"},
    {"id": "deployment-runbook", "stage": "launch", "type": "runbook"},
    {"id": "defect-remediation-plan", "stage": "acceptance", "type": "plan"},
    {"id": "training-session-plan", "stage": "training", "type": "plan"}
  ]
}


path: references/use-case-decision-table.md
content: # Delivery Use-Case Decision Table

| Situation | Template | Why |
|---|---|---|
| First meeting after approval | `kickoff-agenda` | Aligns owners, scope, cadence, and next steps. |
| Need systems/docs/API access | `client-access-checklist` | Captures access needs and owners. |
| Need execution plan | `project-plan` | Structures work into phases and milestones. |
| Track milestone progress | `milestone-tracker` | Shows status, dates, and blockers. |
| Regular client update | `weekly-status-update` | Communicates progress and decisions needed. |
| Decision made | `decision-log` | Records decision, owner, date, and rationale. |
| Risk or issue arises | `risk-and-issue-log` | Tracks impact, owner, mitigation, and status. |
| New scope request | `change-request-intake` | Keeps scope changes controlled. |
| Blocker or prerequisite | `dependency-tracker` | Tracks dependencies and owners. |
| Communication cadence unclear | `stakeholder-communication-plan` | Defines who gets what updates. |
| Prototype ready | `prototype-review-checklist` | Guides structured prototype feedback. |
| Evaluation run complete | `evaluation-run-report` | Captures examples, results, and failures. |
| Test summary needed | `test-results-summary` | Summarizes test status for stakeholders. |
| Formal acceptance | `acceptance-review-packet` | Packages acceptance evidence. |
| Launch planning | `launch-readiness-checklist` | Checks readiness before launch. |
| Monitoring needed | `monitoring-plan` | Defines logs, metrics, owners, and review rhythm. |
| Support terms needed | `support-plan` | Defines support window and scope. |
| Handoff preparation | `handoff-checklist` | Ensures docs, owners, and training are complete. |
| Admin/operator docs | `administrator-runbook` | Gives operating instructions. |
| User/reviewer enablement | `user-reviewer-quickstart` | Gives concise reviewer guidance. |
| After launch | `post-launch-review` | Reviews outcomes, issues, and next steps. |
| Retrospective | `lessons-learned` | Captures what to repeat or change. |
| Project close | `closeout-summary` | Summarizes final status and artifacts. |
| Support request | `support-ticket-intake` | Captures issue details for triage. |
| Escalation path | `escalation-procedure` | Defines severity, contacts, and response path. |
| Deployment execution | `deployment-runbook` | Lists rollout steps and rollback notes. |
| Acceptance defects | `defect-remediation-plan` | Tracks fixes before acceptance. |
| Training session | `training-session-plan` | Plans reviewer/admin enablement. |

Selection rule: choose the artifact matching the current operational event. If the event changes scope, use change request intake before changing project plans or invoices. If acceptance, launch, or closeout is requested, require evidence first.


path: requirements.txt
content: # Branded PDF artifact rendering (scripts/render_pdf.py)
reportlab==4.5.1
pyyaml==6.0.3
# Optional: only needed to also emit a PNG preview montage (scripts/render_pdf.py --png)
pypdfium2==5.8.0
pillow==12.2.0


path: scripts/render_delivery.py
content: #!/usr/bin/env python3
"""List and render agentic development delivery artifacts."""

from __future__ import annotations

import argparse
import json
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
CATALOG_MD = ROOT / "references" / "delivery-catalog.md"
INDEX_JSON = ROOT / "references" / "template-index.json"


def load_index() -> list[dict[str, str]]:
    data = json.loads(INDEX_JSON.read_text(encoding="utf-8"))
    return data["templates"]


def extract_template(template_id: str) -> str:
    text = CATALOG_MD.read_text(encoding="utf-8")
    pattern = re.compile(
        rf"^### {re.escape(template_id)}\n(?P<body>.*?)(?=^### |\Z)",
        re.MULTILINE | re.DOTALL,
    )
    match = pattern.search(text)
    if not match:
        raise KeyError(f"Template not found in delivery-catalog.md: {template_id}")
    return match.group("body").strip()


class SafeDict(dict[str, str]):
    def __missing__(self, key: str) -> str:
        return "{" + key + "}"


def parse_vars(raw_vars: list[str]) -> dict[str, str]:
    values: dict[str, str] = {}
    for raw in raw_vars:
        if "=" not in raw:
            raise ValueError(f"--var must be key=value, got: {raw}")
        key, value = raw.split("=", 1)
        key = key.strip()
        if not key:
            raise ValueError(f"--var key cannot be empty: {raw}")
        values[key] = value
    return values


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--list", action="store_true", help="List available delivery artifact IDs.")
    parser.add_argument("--stage", help="Filter --list by stage.")
    parser.add_argument("--type", dest="artifact_type", help="Filter --list by type.")
    parser.add_argument("--template", help="Artifact ID to render.")
    parser.add_argument("--var", action="append", default=[], help="Placeholder value as key=value.")
    parser.add_argument("--out", default=None,
                        help="Branded PDF output path (default: output/<template>.pdf). "
                             "Same reportlab branding as the contract skill.")
    parser.add_argument("--markdown-out", default=None, help="Also write the filled Markdown here.")
    parser.add_argument("--png", default=None, help="Optional PNG preview montage path.")
    parser.add_argument("--no-pdf", action="store_true", help="Emit Markdown only; skip PDF.")
    parser.add_argument("--no-cover", action="store_true", help="Skip the PDF cover page.")
    parser.add_argument("--title", default=None, help="PDF title (defaults to the template subject/id).")
    parser.add_argument("--doc-type", default=None, help="PDF doc-type label (defaults to the template stage).")
    parser.add_argument("--subtitle", default="", help="Cover subtitle, e.g. \"Prepared for <b>Client</b>\".")
    parser.add_argument("--logo", default=None, help="Logo path (defaults to assets/logo.png).")
    parser.add_argument("--meta", action="append", default=[], help="Cover/letterhead chip as LABEL=VALUE.")
    parser.add_argument("--watermark", default="DEMO DRAFT", help="Watermark text (empty to disable).")
    parser.add_argument("--footer", default="CompleteTech LLC - demonstration artifact", help="Footer text.")
    args = parser.parse_args()

    templates = load_index()
    if args.list:
        for item in templates:
            if args.stage and item["stage"] != args.stage:
                continue
            if args.artifact_type and item["type"] != args.artifact_type:
                continue
            print(f"{item['id']}\t{item['stage']}\t{item['type']}")
        return 0

    if not args.template:
        parser.error("provide --list or --template")

    ids = {item["id"] for item in templates}
    if args.template not in ids:
        print(f"Unknown template: {args.template}", file=sys.stderr)
        return 2

    values = parse_vars(args.var)
    rendered = extract_template(args.template).format_map(SafeDict(values))
    print(rendered)

    if args.no_pdf:
        return 0

    # Branded PDF output: same reportlab engine and CompleteTech branding as the
    # contract skill (see scripts/render_pdf.py). One command -> Markdown + PDF.
    from datetime import date
    here = Path(__file__).resolve().parent
    sys.path.insert(0, str(here))
    try:
        from render_pdf import build_pdf, montage
    except ImportError as exc:
        print(f"[skip PDF] {exc}; run 'pip install -r requirements.txt' to enable PDF.", file=sys.stderr)
        return 0

    item = next((t for t in templates if t["id"] == args.template), {})
    meta = []
    for entry in args.meta:
        if "=" in entry:
            k, v = entry.split("=", 1)
            meta.append((k.strip(), v.strip()))
    if not meta:
        meta = [("REFERENCE", args.template.upper()), ("DATE", date.today().isoformat())]
    title = args.title or item.get("subject") or item.get("title") or args.template.replace("-", " ").title()
    doc_type = args.doc_type if args.doc_type is not None else str(item.get("stage", "")).replace("_", " ").upper()
    logo = Path(args.logo) if args.logo else (ROOT / "assets" / "logo.png")
    out_pdf = Path(args.out) if args.out else (ROOT / "output" / f"{args.template}.pdf")
    out_pdf.parent.mkdir(parents=True, exist_ok=True)
    if args.markdown_out:
        md_out = Path(args.markdown_out)
        md_out.parent.mkdir(parents=True, exist_ok=True)
        md_out.write_text(rendered, encoding="utf-8")
    cfg = {
        "logo": logo if logo.exists() else None,
        "title": title, "eyebrow": "CompleteTech LLC", "doc_type": doc_type,
        "subtitle": args.subtitle, "meta": meta, "doc_id": meta[0][1] if meta else "",
        "watermark": args.watermark, "footer": args.footer,
        "disclaimer": "Demonstration artifact \u2014 replace placeholders with verified facts before use.",
        "cover": not args.no_cover,
    }
    build_pdf(rendered, cfg, out_pdf)
    print(f"PDF: {out_pdf}", file=sys.stderr)
    if args.png:
        montage(out_pdf, Path(args.png))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: scripts/render_pdf.py
content: #!/usr/bin/env python3
"""Shared CompleteTech-branded Markdown -> PDF renderer + full-page PNG montage.

Generalized from agentic_contract_skill/generate_contract.py so the same
CompleteTech LLC branding (logo, palette, cover, letterhead, watermark, footer)
can be applied to every skill in the library.

Usage:
    python render_branded_pdf.py \
        --markdown doc.md --out out.pdf --png out.png \
        --logo /path/assets/logo.png \
        --title "Statement of Work" --eyebrow "COMPLETETECH LLC" \
        --doc-type "PROPOSAL" \
        --subtitle "Prepared for <b>Northwind Trading Co.</b>" \
        --meta "DOCUMENT NO.=PRO-2026-0188" \
        --meta "DATE=2026-05-24" \
        --meta "PREPARED BY=CompleteTech LLC" \
        --watermark "DEMO DRAFT" \
        --footer "CompleteTech LLC - Innovation at Every Integration"
"""

from __future__ import annotations

import argparse
import html
import re
from pathlib import Path
from typing import Any, Dict, List, Optional, Sequence

from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT
from reportlab.lib.pagesizes import LETTER
from reportlab.lib.units import inch
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen import canvas as pdf_canvas
from reportlab.platypus import (
    Flowable, ListFlowable, ListItem, PageBreak, Paragraph,
    SimpleDocTemplate, Spacer, Table, TableStyle,
)
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet

# --------------------------------------------------------------------------- #
# Brand palette (CompleteTech LLC defaults, matching the contract skill)
# --------------------------------------------------------------------------- #
BRAND = {
    "accent": "#1E3A8A",
    "accent_dark": "#0F172A",
    "accent_soft": "#EEF2FF",
    "muted": "#64748B",
    "border": "#E2E8F0",
    "zebra": "#F8FAFC",
    "brand_name": "CompleteTech",
    "brand_tagline": "Innovation at Every Integration",
    "contact": "complete.tech  ·  [REDACTED_SECRET]",
}


def hex_color(raw: Optional[str], fallback: str) -> colors.Color:
    try:
        return colors.HexColor(raw or fallback)
    except Exception:
        return colors.HexColor(fallback)


def palette() -> Dict[str, colors.Color]:
    return {
        "accent": hex_color(BRAND["accent"], "#1E3A8A"),
        "accent_dark": hex_color(BRAND["accent_dark"], "#0F172A"),
        "accent_soft": hex_color(BRAND["accent_soft"], "#EEF2FF"),
        "muted": hex_color(BRAND["muted"], "#64748B"),
        "border": hex_color(BRAND["border"], "#E2E8F0"),
        "zebra": hex_color(BRAND["zebra"], "#F8FAFC"),
        "ink": colors.HexColor("#0F172A"),
        "ink_soft": colors.HexColor("#1E293B"),
        "white": colors.white,
    }


# --------------------------------------------------------------------------- #
# Logo
# --------------------------------------------------------------------------- #
def draw_logo(c: pdf_canvas.Canvas, x: float, y: float, size: float,
              logo_path: Optional[Path], pal: Dict[str, colors.Color],
              on_dark: bool = False) -> None:
    if logo_path and logo_path.exists():
        try:
            c.drawImage(ImageReader(str(logo_path)), x, y, width=size, height=size,
                        preserveAspectRatio=True, mask="auto")
            return
        except Exception:
            pass
    radius = size * 0.18
    c.saveState()
    fill = pal["white"] if on_dark else pal["accent"]
    c.setFillColor(fill)
    c.roundRect(x, y, size, size, radius, stroke=0, fill=1)
    c.setFillColor(pal["accent"] if on_dark else pal["white"])
    c.setFont("Helvetica-Bold", size * 0.5)
    c.drawCentredString(x + size / 2.0, y + size / 2.0 - size * 0.18, "CT")
    c.restoreState()


class LogoFlowable(Flowable):
    def __init__(self, logo_path: Optional[Path], pal: Dict[str, colors.Color], size: float):
        super().__init__()
        self.logo_path = logo_path
        self.pal = pal
        self.size = size
        self.width = size
        self.height = size * 0.5  # logo art is ~2:1, keep cover tidy

    def wrap(self, aW: float, aH: float) -> tuple[float, float]:
        return self.size, self.size * 0.52

    def draw(self):
        if self.logo_path and self.logo_path.exists():
            try:
                img = ImageReader(str(self.logo_path))
                iw, ih = img.getSize()
                ratio = ih / iw
                w = self.size
                h = w * ratio
                self.canv.drawImage(img, 0, 0, width=w, height=h,
                                    preserveAspectRatio=True, mask="auto")
                self.height = h
                return
            except Exception:
                pass
        draw_logo(self.canv, 0, 0, self.size * 0.4, self.logo_path, self.pal)


# --------------------------------------------------------------------------- #
# Styles
# --------------------------------------------------------------------------- #
def make_styles(pal: Dict[str, colors.Color]) -> Dict[str, ParagraphStyle]:
    base = getSampleStyleSheet()
    return {
        "CoverEyebrow": ParagraphStyle("CoverEyebrow", parent=base["BodyText"],
            fontName="Helvetica-Bold", fontSize=8.5, leading=11, alignment=TA_CENTER,
            textColor=pal["accent"], spaceAfter=8),
        "CoverDocType": ParagraphStyle("CoverDocType", parent=base["BodyText"],
            fontName="Helvetica-Bold", fontSize=8, leading=10, alignment=TA_CENTER,
            textColor=pal["muted"], spaceAfter=6),
        "CoverTitle": ParagraphStyle("CoverTitle", parent=base["Title"],
            fontName="Helvetica-Bold", fontSize=27, leading=32, alignment=TA_CENTER,
            textColor=pal["ink"], spaceAfter=10),
        "CoverSubtitle": ParagraphStyle("CoverSubtitle", parent=base["BodyText"],
            fontName="Times-Roman", fontSize=12.5, leading=17, alignment=TA_CENTER,
            textColor=pal["ink_soft"], spaceAfter=4),
        "CoverFieldLabel": ParagraphStyle("CoverFieldLabel", parent=base["BodyText"],
            fontName="Helvetica-Bold", fontSize=7.5, leading=10, alignment=TA_LEFT,
            textColor=pal["muted"], spaceAfter=2),
        "CoverFieldValue": ParagraphStyle("CoverFieldValue", parent=base["BodyText"],
            fontName="Helvetica-Bold", fontSize=11, leading=14, alignment=TA_LEFT,
            textColor=pal["ink"], spaceAfter=0),
        "CoverDisclaimer": ParagraphStyle("CoverDisclaimer", parent=base["BodyText"],
            fontName="Times-Italic", fontSize=9, leading=12.5, alignment=TA_CENTER,
            textColor=pal["muted"]),
        "Title": ParagraphStyle("Title", parent=base["Title"], fontName="Helvetica-Bold",
            fontSize=17, leading=21, alignment=TA_LEFT, spaceAfter=12, textColor=pal["ink"]),
        "Heading2": ParagraphStyle("Heading2", parent=base["Heading2"], fontName="Helvetica-Bold",
            fontSize=13, leading=17, spaceBefore=14, spaceAfter=7, textColor=pal["ink"]),
        "Heading3": ParagraphStyle("Heading3", parent=base["Heading3"], fontName="Helvetica-Bold",
            fontSize=10.8, leading=14, spaceBefore=8, spaceAfter=4, textColor=pal["accent"]),
        "Body": ParagraphStyle("Body", parent=base["BodyText"], fontName="Times-Roman",
            fontSize=9.8, leading=13.5, alignment=TA_JUSTIFY, spaceAfter=6, textColor=pal["ink_soft"]),
        "Bullet": ParagraphStyle("Bullet", parent=base["BodyText"], fontName="Times-Roman",
            fontSize=9.8, leading=13.2, leftIndent=8, spaceAfter=3, textColor=pal["ink_soft"]),
        "TableCell": ParagraphStyle("TableCell", parent=base["BodyText"], fontName="Times-Roman",
            fontSize=9, leading=12, spaceAfter=0, textColor=pal["ink_soft"]),
        "TableHeader": ParagraphStyle("TableHeader", parent=base["BodyText"], fontName="Helvetica-Bold",
            fontSize=8.5, leading=11, textColor=pal["white"], spaceAfter=0),
    }


# --------------------------------------------------------------------------- #
# Markdown -> flowables (subset: #/##/###, paragraphs, - bullets, tables,
# > callouts, **bold**, [PAGE_BREAK])
# --------------------------------------------------------------------------- #
BR = "\x00BR\x00"
SECTION_NUMBER_RE = re.compile(r"^\s*(\d+)\.\s+(.+)$")


def inline_markup(text: str) -> str:
    text = text.replace("<br/>", BR)
    esc = html.escape(text, quote=False)
    esc = esc.replace(BR, "<br/>")
    esc = re.sub(r"\*\*(.+?)\*\*", r"<b>\1</b>", esc)
    esc = re.sub(r"__(.+?)__", r"<b>\1</b>", esc)
    return esc


def color_to_hex(c: colors.Color) -> str:
    return "#%02X%02X%02X" % (int(round(c.red * 255)), int(round(c.green * 255)), int(round(c.blue * 255)))


def numbered_heading_markup(text: str, pal: Dict[str, colors.Color]) -> str:
    m = SECTION_NUMBER_RE.match(text)
    if not m:
        return inline_markup(text)
    number, label = m.group(1), m.group(2)
    return (f'<font name="Helvetica-Bold" size="20" color="{color_to_hex(pal["accent"])}">{int(number):02d}</font>'
            f'&nbsp;&nbsp;<font color="{color_to_hex(pal["muted"])}">|</font>&nbsp;&nbsp;{inline_markup(label)}')


def is_table_sep(cells: Sequence[str]) -> bool:
    return all(re.fullmatch(r":?-{3,}:?", c.strip()) for c in cells if c.strip())


def parse_table_rows(lines: Sequence[str]) -> List[List[str]]:
    rows = []
    for line in lines:
        cells = [c.strip() for c in line.strip().strip("|").split("|")]
        if is_table_sep(cells):
            continue
        rows.append(cells)
    return rows


def col_widths(rows, available):
    ncols = max((len(r) for r in rows), default=1)
    if ncols == 1:
        return [available]
    if ncols == 2:
        return [available * 0.34, available * 0.66]
    return [available / ncols] * ncols


def make_table(lines, styles, available, pal) -> Table:
    rows = parse_table_rows(lines) or [[""]]
    ncols = max(len(r) for r in rows)
    rows = [list(r) + [""] * (ncols - len(r)) for r in rows]
    data = [[Paragraph(inline_markup(c), styles["TableHeader"] if i == 0 else styles["TableCell"])
             for c in row] for i, row in enumerate(rows)]
    t = Table(data, colWidths=col_widths(rows, available), repeatRows=1, hAlign="LEFT")
    t.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), pal["accent"]),
        ("TEXTCOLOR", (0, 0), (-1, 0), pal["white"]),
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
        ("LINEBELOW", (0, 0), (-1, 0), 0.6, pal["accent_dark"]),
        ("LINEBELOW", (0, -1), (-1, -1), 0.4, pal["border"]),
        ("LEFTPADDING", (0, 0), (-1, -1), 8), ("RIGHTPADDING", (0, 0), (-1, -1), 8),
        ("TOPPADDING", (0, 0), (-1, 0), 7), ("BOTTOMPADDING", (0, 0), (-1, 0), 7),
        ("TOPPADDING", (0, 1), (-1, -1), 6), ("BOTTOMPADDING", (0, 1), (-1, -1), 6),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [pal["white"], pal["zebra"]]),
        ("LINEABOVE", (0, 1), (-1, -1), 0.25, pal["border"]),
    ]))
    return t


def make_callout(quote_lines, styles, available, pal) -> Table:
    para = Paragraph(inline_markup(" ".join(quote_lines)).strip(),
                     ParagraphStyle("CalloutBody", parent=styles["Body"], fontName="Times-Italic",
                                    textColor=pal["ink_soft"], leading=13.5, fontSize=9.5,
                                    alignment=TA_LEFT, spaceAfter=0))
    t = Table([[para]], colWidths=[available], hAlign="LEFT")
    t.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, -1), pal["accent_soft"]),
        ("LINEBEFORE", (0, 0), (0, -1), 3, pal["accent"]),
        ("LEFTPADDING", (0, 0), (-1, -1), 14), ("RIGHTPADDING", (0, 0), (-1, -1), 14),
        ("TOPPADDING", (0, 0), (-1, -1), 9), ("BOTTOMPADDING", (0, 0), (-1, -1), 9),
    ]))
    return t


def is_block_start(line: str) -> bool:
    s = line.strip()
    return (s.startswith("#") or s.startswith("- ") or s.startswith("|")
            or s.startswith("> ") or s == "[PAGE_BREAK]")


def paragraph_text(lines: Sequence[str]) -> str:
    parts = [(line.strip() + "<br/>") if line.endswith("  ") else line.strip() for line in lines]
    return re.sub(r"<br/>\s+", "<br/>", " ".join(parts))


def markdown_to_story(md, styles, available, pal) -> List[Any]:
    story: List[Any] = []
    lines = md.splitlines()
    i = 0
    while i < len(lines):
        raw = lines[i]
        line = raw.strip()
        if not line:
            i += 1
            continue
        if line == "[PAGE_BREAK]":
            story.append(PageBreak())
            i += 1
            continue
        if line.startswith("# "):
            story.append(Paragraph(inline_markup(line[2:].strip()), styles["Title"]))
            story.append(Spacer(1, 0.05 * inch))
            i += 1
            continue
        if line.startswith("## "):
            story.append(Paragraph(numbered_heading_markup(line[3:].strip(), pal), styles["Heading2"]))
            i += 1
            continue
        if line.startswith("### "):
            story.append(Paragraph(inline_markup(line[4:].strip()), styles["Heading3"]))
            i += 1
            continue
        if line.startswith("> "):
            q = []
            while i < len(lines) and lines[i].strip().startswith("> "):
                q.append(lines[i].strip()[2:])
                i += 1
            story.append(make_callout(q, styles, available, pal))
            continue
        if line.startswith("- "):
            items = []
            while i < len(lines) and lines[i].strip().startswith("- "):
                items.append(ListItem(Paragraph(inline_markup(lines[i].strip()[2:].strip()), styles["Bullet"]),
                                      leftIndent=12, value="●", bulletColor=pal["accent"]))
                i += 1
            story.append(ListFlowable(items, bulletType="bullet", bulletFontName="Helvetica-Bold",
                                      bulletFontSize=6, leftIndent=20, bulletColor=pal["accent"]))
            story.append(Spacer(1, 0.04 * inch))
            continue
        if line.startswith("|"):
            tl = []
            while i < len(lines) and lines[i].strip().startswith("|"):
                tl.append(lines[i])
                i += 1
            story.append(make_table(tl, styles, available, pal))
            story.append(Spacer(1, 0.10 * inch))
            continue
        para = [raw]
        i += 1
        while i < len(lines) and lines[i].strip() and not is_block_start(lines[i]):
            para.append(lines[i])
            i += 1
        story.append(Paragraph(inline_markup(paragraph_text(para)), styles["Body"]))
    return story


# --------------------------------------------------------------------------- #
# Page furniture
# --------------------------------------------------------------------------- #
def draw_watermark(c, w, h, text):
    if not text:
        return
    c.saveState()
    try:
        c.setFillColor(colors.Color(0.7, 0.7, 0.7, alpha=0.16))
    except TypeError:
        c.setFillColor(colors.HexColor("#D1D5DB"))
    c.translate(w / 2.0, h / 2.0)
    c.rotate(42)
    c.setFont("Helvetica-Bold", 58)
    c.drawCentredString(0, 0, text)
    c.restoreState()


def draw_letterhead_band(c, meta, w, h, pal, logo_path):
    band = 0.95 * inch
    c.saveState()
    c.setFillColor(pal["accent_dark"])
    c.rect(0, h - band, w, band, stroke=0, fill=1)
    c.setFillColor(pal["accent"])
    c.rect(0, h - band - 0.04 * inch, w, 0.04 * inch, stroke=0, fill=1)
    pad = 0.72 * inch
    ls = 0.52 * inch
    draw_logo(c, pad, h - band + (band - ls) / 2.0, ls, logo_path, pal, on_dark=True)
    nx = pad + ls + 0.18 * inch
    c.setFillColor(pal["white"])
    c.setFont("Helvetica-Bold", 13.5)
    c.drawString(nx, h - 0.42 * inch, BRAND["brand_name"])
    c.setFont("Helvetica", 8.5)
    c.setFillColor(colors.Color(1, 1, 1, alpha=0.78))
    c.drawString(nx, h - 0.58 * inch, BRAND["brand_tagline"])
    c.setFont("Helvetica", 7.5)
    c.setFillColor(colors.Color(1, 1, 1, alpha=0.62))
    c.drawString(nx, h - 0.74 * inch, BRAND["contact"])
    right = w - pad
    pairs = meta[:2]
    ys = [(0.36, 0.50), (0.66, 0.80)]
    for (label, value), (ly, vy) in zip(pairs, ys):
        c.setFillColor(colors.Color(1, 1, 1, alpha=0.7))
        c.setFont("Helvetica", 7.2)
        c.drawRightString(right, h - ly * inch, label.upper())
        c.setFillColor(pal["white"])
        c.setFont("Helvetica-Bold", 10.5)
        c.drawRightString(right, h - vy * inch, value)
    c.restoreState()


def draw_compact_header(c, doc_title, doc_id, w, h, pal, logo_path):
    pad = 0.72 * inch
    ls = 0.30 * inch
    draw_logo(c, pad, h - 0.62 * inch, ls, logo_path, pal, on_dark=False)
    tx = pad + ls + 0.12 * inch
    c.saveState()
    c.setFillColor(pal["ink"])
    c.setFont("Helvetica-Bold", 9)
    c.drawString(tx, h - 0.46 * inch, BRAND["brand_name"])
    c.setFillColor(pal["muted"])
    c.setFont("Helvetica", 7.5)
    c.drawString(tx, h - 0.58 * inch, BRAND["brand_tagline"])
    right = w - pad
    c.setFillColor(pal["ink"])
    c.setFont("Helvetica-Bold", 8.8)
    c.drawRightString(right, h - 0.46 * inch, doc_title)
    c.setFillColor(pal["muted"])
    c.setFont("Helvetica", 7.5)
    c.drawRightString(right, h - 0.58 * inch, doc_id)
    c.setStrokeColor(pal["accent"])
    c.setLineWidth(0.8)
    c.line(pad, h - 0.74 * inch, right, h - 0.74 * inch)
    c.restoreState()


def draw_footer(c, doc, footer_text, doc_id, w, pal):
    pad = 0.72 * inch
    right = w - pad
    y = 0.46 * inch
    c.saveState()
    c.setStrokeColor(pal["border"])
    c.setLineWidth(0.5)
    c.line(pad, y + 0.18 * inch, right, y + 0.18 * inch)
    c.setFillColor(pal["accent"])
    c.rect(pad, y + 0.18 * inch, 0.32 * inch, 0.012 * inch, stroke=0, fill=1)
    c.setFillColor(pal["muted"])
    c.setFont("Helvetica", 7.4)
    c.drawString(pad, y, doc_id)
    c.drawCentredString(w / 2, y, footer_text[:120])
    c.setFillColor(pal["ink"])
    c.setFont("Helvetica-Bold", 7.6)
    c.drawRightString(right, y, f"Page {doc.page}")
    c.restoreState()


def page_drawer(cfg, pal, first_page, cover_first):
    def draw(c, doc):
        w, h = doc.pagesize
        draw_watermark(c, w, h, cfg["watermark"])
        if first_page:
            if cover_first:
                draw_footer(c, doc, cfg["footer"], cfg["doc_id"], w, pal)
                return
            draw_letterhead_band(c, cfg["meta"], w, h, pal, cfg["logo"])
        else:
            draw_compact_header(c, cfg["title"], cfg["doc_id"], w, h, pal, cfg["logo"])
        draw_footer(c, doc, cfg["footer"], cfg["doc_id"], w, pal)
    return draw


def cover_flowables(cfg, styles, available, pal) -> List[Any]:
    story: List[Any] = [Spacer(1, 1.25 * inch)]
    logo = LogoFlowable(cfg["logo"], pal, size=2.6 * inch)
    logo.hAlign = "CENTER"
    story.append(logo)
    story.append(Spacer(1, 0.35 * inch))
    if cfg["eyebrow"]:
        story.append(Paragraph(cfg["eyebrow"].upper(), styles["CoverEyebrow"]))
    if cfg["doc_type"]:
        story.append(Paragraph(cfg["doc_type"].upper(), styles["CoverDocType"]))
    story.append(Paragraph(cfg["title"], styles["CoverTitle"]))
    if cfg["subtitle"]:
        story.append(Paragraph(cfg["subtitle"], styles["CoverSubtitle"]))
    story.append(Spacer(1, 0.55 * inch))

    def field(label, value):
        return [Paragraph(label.upper(), styles["CoverFieldLabel"]),
                Paragraph(html.escape(value or "TBD"), styles["CoverFieldValue"])]

    meta = cfg["meta"][:3]
    if meta:
        row = [[field(label, value) for label, value in meta]]
        cw = (available * 0.9) / len(meta)
        chip = Table(row, colWidths=[cw] * len(meta), hAlign="CENTER")
        chip.setStyle(TableStyle([
            ("BACKGROUND", (0, 0), (-1, -1), pal["accent_soft"]),
            ("LINEABOVE", (0, 0), (-1, 0), 2, pal["accent"]),
            ("LEFTPADDING", (0, 0), (-1, -1), 16), ("RIGHTPADDING", (0, 0), (-1, -1), 16),
            ("TOPPADDING", (0, 0), (-1, -1), 14), ("BOTTOMPADDING", (0, 0), (-1, -1), 14),
            ("VALIGN", (0, 0), (-1, -1), "TOP"),
        ]))
        story.append(chip)
    story.append(Spacer(1, 0.7 * inch))
    if cfg["disclaimer"]:
        story.append(Paragraph(cfg["disclaimer"], styles["CoverDisclaimer"]))
    story.append(PageBreak())
    return story


def build_pdf(md, cfg, out_path: Path):
    pal = palette()
    styles = make_styles(pal)
    cover = cfg["cover"]
    lm = rm = 0.72 * inch
    top = 0.82 * inch if cover else 1.30 * inch
    doc = SimpleDocTemplate(str(out_path), pagesize=LETTER, leftMargin=lm, rightMargin=rm,
                            topMargin=top, bottomMargin=0.82 * inch, title=cfg["title"],
                            author=BRAND["brand_name"])
    available = LETTER[0] - lm - rm
    story: List[Any] = []
    if cover:
        story.extend(cover_flowables(cfg, styles, available, pal))
    story.extend(markdown_to_story(md, styles, available, pal))
    out_path.parent.mkdir(parents=True, exist_ok=True)
    doc.build(story,
              onFirstPage=page_drawer(cfg, pal, first_page=True, cover_first=cover),
              onLaterPages=page_drawer(cfg, pal, first_page=False, cover_first=cover))


# --------------------------------------------------------------------------- #
# PDF -> PNG montage (all pages, full size, uncropped)
# --------------------------------------------------------------------------- #
def montage(pdf_path: Path, png_path: Path, scale: float = 2.0,
            cols: Optional[int] = None, bg=(244, 246, 251), gutter: int = 26,
            border=(210, 217, 230)) -> None:
    import pypdfium2 as pdfium
    from PIL import Image

    doc = pdfium.PdfDocument(str(pdf_path))
    pages = []
    for i in range(len(doc)):
        page = doc[i]
        bmp = page.render(scale=scale)
        pages.append(bmp.to_pil().convert("RGB"))
    n = len(pages)
    if cols is None:
        cols = 1 if n == 1 else (2 if n <= 4 else 3)
    rows = (n + cols - 1) // cols
    pw, ph = pages[0].size
    bw = 1  # page border width
    cell_w, cell_h = pw + 2 * bw, ph + 2 * bw
    W = gutter + cols * (cell_w + gutter)
    H = gutter + rows * (cell_h + gutter)
    canvas = Image.new("RGB", (W, H), bg)
    bordered = Image.new("RGB", (cell_w, cell_h), border)
    for idx, p in enumerate(pages):
        r, cc = divmod(idx, cols)
        x = gutter + cc * (cell_w + gutter)
        y = gutter + r * (cell_h + gutter)
        tile = bordered.copy()
        tile.paste(p, (bw, bw))
        canvas.paste(tile, (x, y))
    png_path.parent.mkdir(parents=True, exist_ok=True)
    canvas.save(str(png_path))
    print(f"PNG montage: {png_path} ({n} page(s), {W}x{H})")


# --------------------------------------------------------------------------- #
def parse_meta(items: List[str]) -> List[tuple]:
    out = []
    for it in items:
        if "=" in it:
            k, v = it.split("=", 1)
            out.append((k.strip(), v.strip()))
    return out


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--markdown", required=True)
    ap.add_argument("--out", required=True)
    ap.add_argument("--png", default=None)
    ap.add_argument("--logo", default=None)
    ap.add_argument("--title", default="Document")
    ap.add_argument("--eyebrow", default="CompleteTech LLC")
    ap.add_argument("--doc-type", default="")
    ap.add_argument("--subtitle", default="")
    ap.add_argument("--meta", action="append", default=[])
    ap.add_argument("--doc-id", default="")
    ap.add_argument("--watermark", default="DEMO DRAFT")
    ap.add_argument("--footer", default="CompleteTech LLC - Innovation at Every Integration - demonstration artifact")
    ap.add_argument("--disclaimer", default="Demonstration artifact generated with realistic placeholder data — not a real client engagement.")
    ap.add_argument("--no-cover", action="store_true")
    ap.add_argument("--scale", type=float, default=2.0)
    ap.add_argument("--cols", type=int, default=None)
    args = ap.parse_args()

    meta = parse_meta(args.meta)
    cfg = {
        "logo": Path(args.logo).resolve() if args.logo else None,
        "title": args.title, "eyebrow": args.eyebrow, "doc_type": args.doc_type,
        "subtitle": args.subtitle, "meta": meta,
        "doc_id": args.doc_id or (meta[0][1] if meta else ""),
        "watermark": args.watermark, "footer": args.footer,
        "disclaimer": args.disclaimer, "cover": not args.no_cover,
    }
    md = Path(args.markdown).read_text(encoding="utf-8")
    out_pdf = Path(args.out).resolve()
    build_pdf(md, cfg, out_pdf)
    print(f"PDF: {out_pdf}")
    if args.png:
        montage(out_pdf, Path(args.png).resolve(), scale=args.scale, cols=args.cols)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)