What does Portfolio One Shot do?
Purpose
Turn a directory of real work into a portfolio that can be opened, read by machines, and independently checked — without the model writing a single line of fictional project copy.
The three invariants, in priority order:
- Zero fabrication. Every displayed fact must trace back to a file, a README excerpt, or a value the user supplied. Missing fields render as visible gaps. Never invent project names, roles, metrics, clients, quotes, results, or images.
- Independent verification. Run the verifier on the output before reporting success. List failures first, passes second. Never present a green criteria list as proof that the portfolio looks good — visual judgement belongs to the user, by eye.
- One command.
allperforms scan → config → build → verify. A second command is a defect, not a workflow.
When to use
- The user says 一键生成作品集 / 做个作品集 / 把我的项目做成作品集 / build a portfolio from this folder.
- The user hands over a directory of images, videos, or project folders and wants them presented.
- The user wants to regenerate a portfolio after adding or removing work.
- The user wants to check whether an existing portfolio has missing evidence.
Do not use when the user wants a decorative demo site with made-up content, a specific art-directed visual style, or a hosted platform integration. Those are different jobs. Do not silently switch to this skill's zero-fabrication rules in that case — say the difference out loud.
The one command
python scripts/portfolio.py all <SOURCE_DIR> --out <OUTPUT_DIR> --mode <case_study|gallery>
Example:
python scripts/portfolio.py all "C:/work/my-projects" --out "C:/work/portfolio-dist" --mode gallery
The command writes index.html, portfolio.txt, portfolio.md, projects.json, assets/, build-report.json, and verify-report.md into the output directory, then prints the verifier verdict. Open index.html for the user with the file-presentation tool. Put the visual deliverable first in the list.
To rebuild after the user edits projects.json, run build and verify only:
python scripts/portfolio.py build <OUTPUT_DIR>/projects.json --out <OUTPUT_DIR> --mode gallery
python scripts/portfolio.py verify <OUTPUT_DIR>
Workflow
Phase 0 · Decide the mode, do not guess silently
Two modes exist because the source research found the two domains demand opposite things (see references/content-spec.md §Domain split).
| Mode | For | Text density | Layout |
|---|---|---|---|
case_study | UX, product, design, engineering, research | Long narrative; seven required field groups | One project per section, images interleaved |
gallery | Game art, illustration, concept art, photography, animation | Minimal words; let the work speak | Image-forward grid, lightbox on click |
If the user has not chosen, infer from the material and say the inference in one sentence before running — then let one word from the user override it. Never hard-code a default into someone's portfolio.
Phase 1 · Scan real material
scan enumerates subdirectories as projects. For each project it records:
- Directory name as the project title candidate (a fact, not an invention).
- Every image and video file with byte size, SHA-256 prefix, and pixel dimensions.
- The first paragraph of
README.md/project.md/about.mdas a verbatimsummarycandidate, with a_sourcesentry naming the file and line range. - A
missinglist of fields no file could supply.
Files with unknown extensions stay in manifest.json as unclassified rather than being dropped. Never delete, rename, move, or resize source files. Write only into the output directory.
Phase 2 · Build
build reads projects.json (schema in references/config-schema.md) and renders:
index.html— light theme, responsive, system font stack, no CDN dependencies, no flashy template. Every<img>carriesalt,width,height,loading="lazy". Images use relative paths underassets/.portfolio.txt— plain-text project summary for ATS parsing and copy-paste into a resume.portfolio.md— the same facts as Markdown for README or email.- A visible warning banner stating exactly how many fields are missing, so a skeleton can never be mistaken for a finished portfolio.
When --embed is set, inline only images under 10 KB as base64 and keep larger ones external; print a warning for every file that would have exceeded the threshold. Base64 grows payload by about 33 percent and cannot be lazy-loaded — see references/content-spec.md §Image policy.
Phase 3 · Verify before reporting
verify checks field completeness, image existence and decodability, meaningful alt text, absence of placeholder-image hosts, link shape, HTML structure, and plain-text parity. It writes verify-report.md with Failures first, Passes second, then Limitations / unverified.
Exit code 1 means red items exist. Report that honestly. Do not edit the criteria to make them pass, do not retry a failing generation channel, and do not hide a limitation.
For a positive/negative control, run python scripts/portfolio.py verify <dir> --self-test — it constructs one deliberately broken project and one complete project in memory and confirms the verifier flags the first and clears the second. If the negative control passes green, the verifier is broken; fix the verifier, not the fixture.
Phase 4 · Hand off
Present index.html first so the user can open it, then verify-report.md, then projects.json. In the text reply: give the verdict, the exact number of missing fields, and the single next action (fill projects.json). Do not claim the portfolio looks good. Do not claim it is ready to send while the warning banner is present.
Zero-fabrication contract
Hard rules, all derived from documented failure modes in references/content-spec.md:
- Never write project prose, captions, roles, metrics, outcomes, credits, or client names that are absent from the input. A missing field renders as
[MISSING: field]— conspicuous, never silently blank, never paraphrased into a plausible sentence. - Never use placeholder image hosts (
picsum.photos,placehold.co,lorem, stock URLs) as stand-ins for real work. A project with no images renders an explicit empty-media block. - Never quote a metric without a
_sourcesentry. If the user supplies a metric, recordsource: "user-supplied"; do not upgrade it to "verified". - Never invent a studio name, personal bio, or contact details. Leave them empty and list them as failures.
- When a README is used, copy the paragraph verbatim and record its file and line range. Do not polish, shorten, or rewrite it.
- Do not present AI-assisted wording as the user's own decision-making. Process and reflection fields stay empty until the user supplies them.
Multi-version output
The same projects.json supports targeted versions. Add a select list of project ids, or pass --only id1,id2 to build, to produce a version aimed at one audience. Reordering is a config change, never an automatic rewrite. This follows the documented split between a website for discovery and a PDF or deck for applications.
Gotchas
- Do not judge from a fragment. Read the whole README, the whole
projects.json, and the whole verifier report before concluding anything is complete or broken. - Criteria passing is not quality. A green verifier means the file structure is sound. Aesthetics require the user's eyes.
- Do not fill gaps to make the page look finished. A visibly incomplete page is the correct output when the user has not supplied the facts.
- Do not touch personal directories. Scan is read-only on the source. All writes go to the output directory.
- Do not scope-creep into PDF generation, hosting, or GitHub API access. Those need an explicit decision from the user.
- Image dimensions matter. Emitting
widthandheightprevents layout shift. If Pillow is unavailable, fall back to header parsing and print a visible warning; never silently degrade. - One folder is one project. If the user's material is flat, ask for a grouping or use
--flatto make one project containing all media. Do not invent groupings.
Resources
| Path | Role |
|---|---|
scripts/portfolio.py | Single CLI: scan, build, verify, all, --self-test. Standard library plus optional Pillow. |
references/content-spec.md | Research-backed content rules, domain split, image policy, and source URLs. Load before changing field definitions or templates. |
references/config-schema.md | Full projects.json schema, field meanings, and what counts as a source. |
assets/template.html | Light-theme responsive template with both modes. Replace {{TOKEN}} placeholders only; do not rewrite the CSS to chase a look. |
assets/projects.sample.json | A clearly labelled DEMO config. Never ship it as the user's real portfolio. |