Releases: thethunderbolt/fullstack-forge-skill
Release list
Fullstack Forge v0.2.0
Release notes — v0.2.0
Fullstack Forge v0.2.0 adds Build mode: a second, additive product mode that guides an AI coding
agent while it starts a project or implements a feature, so production quality is built in from the
beginning instead of discovered only by the final audit. The existing audit, fix, verify, and ship
system is unchanged and remains the independent backstop for everything Build mode produces.
What Fullstack Forge does
Build mode: forge new frames a product once (users, business rules, risk-class inputs, stack
rationale, explicit non-goals such as "no Redis", design direction, initial feature list);
forge feature <slug> then runs each feature through frame → plan → implement → check → done with
proportional rigor. Audit mode: forge <section> audit|fix|verify|report, forge all ..., and the
fail-closed forge ship release gate, exactly as in v0.1.x.
Build mode in brief
- Three risk tiers —
light(one-shot),standard,high— computed from recorded inputs. The CLI
enforces a tier floor: identity, payments, tenancy, uploads, AI, migration, secrets, session,
cryptography, SSRF, and destructive-operation triggers escalate a feature tohighwith the
triggers recorded; a lower tier requires a recorded--reasonoverride. The floor is re-applied
atplan,check, anddone. checkanddoneare enforced: statuses are derived by the CLI from real executions
(changed-scope analyzers, argv project commands behind--allow-run, structural checks), never
written by the agent, and re-verified by per-file SHA-256 on every reload. A reloaded
discipline:*PASS— which the deriver never produces — is demoted toNOT_VERIFIED.accept-riskrequires a reason, is recorded immutably, is rendered distinctly (never asPASS),
and is refused for required security controls at high tier (auth,authorization,security,
privacy,tenancy,uploads,payments).- 42 hand-authored build-discipline briefs ("decide before coding" + "evidence to produce while
building"), one per audit module, with CI-enforced exact slug-set coverage. Concluding that a
project needs no cache, no Redis, no queue, and no microservices is a first-class recorded
outcome. - Resumable state under
.forge/build/with fail-closed schema validation, redaction of
agent-authored text, a criterion-scoped repair-cycle cap of 2, and terminalblocked/
abandonededges. - Build state satisfies zero
forge shiporforge all auditgates. Both always re-derive
their own evidence independently.
Supported agents
Codex and generic Agent Skills clients (.agents/skills), Claude Code (.claude/skills), Google
Antigravity (project .agents/skills, user ~/.gemini/config/skills), Gemini CLI
(.gemini/skills), Cursor (.cursor/skills), Windsurf (.windsurf/skills), and GitHub Copilot
(.github/skills). The two new command skills, forge-new and forge-feature, ship to all six
generated platform roots alongside the 42 audit skills.
Installation
npm install --save-dev github:thethunderbolt/fullstack-forge-skill#v0.2.0
npx forge init allOr download the platform archive from the release, verify it against SHA256SUMS.txt, and extract
at the project root. Existing v0.1.x installations upgrade with forge update <platform>; the
upgrade is additive and invalidates no existing .forge/ artifact.
Commands
Build mode: forge new, forge feature <slug> [frame|plan|check|done|accept-risk|abandon|status],
forge resume. Audit mode: unchanged (forge <section> <audit|fix|verify|report>, forge all ...,
forge ship, platform lifecycle, and forge tool <name>). See docs/BUILD_MODE.md,
docs/COMMANDS.md, and docs/CLI_REFERENCE.md.
Evidence and safety model
Unchanged foundations: findings use the shared schema with PASS, FAIL, WARNING,
NOT_APPLICABLE, NOT_VERIFIED, and BLOCKED; a PASS requires affirmative evidence; missing
evidence is never converted to a pass. Build mode adds: enforcement honesty (frame/plan are
recorded guidance, only check/done are machine-enforced), layered state-trust defenses, and
strict ship-gate isolation. See docs/SECURITY_MODEL.md for the build-mode threat surface.
Distribution files
Nine platform ZIP archives (fullstack-forge-<platform>-v0.2.0.zip plus the all bundle),
SHA256SUMS.txt, and manifest.json, built deterministically and attested by the release workflow.
The final post-release verification asset is generated by the tag workflow after draft-asset
verification and is not part of tagged source.
Known limitations
- The CLI cannot force analysis quality in
frameandplan; they are recorded guidance. - Most discipline criteria resolve to
NOT_VERIFIEDor a reasonedNOT_APPLICABLEwithout runtime
evidence; the independent backstop remainsforge all auditandforge ship. - Build state files are local and writable; hand-edited statuses can misrepresent only build-mode
bookkeeping, never a ship or audit decision. - The social preview image requires a manual upload step documented in
docs/RELEASING.md; do not
assume it is configured.
Attribution
Build mode is original work grounded in the project's existing primary-standards research (recorded
in research/SOURCES.md and THIRD_PARTY_NOTICES.md). No third-party skill text was copied.
Fullstack Forge v0.1.10
Release notes — v0.1.10
Discovery evidence classification and specification traceability milestone.
Two things a report can get wrong without ever looking dishonest: claiming a capability exists
because a word appeared somewhere, and claiming compliance nobody can check. This release closes
both.
Discovery records what kind of evidence each signal is
Previously a capability keyword was a capability keyword. A mention of Stripe in a README, in a test
fixture, in a generated platform copy of Forge's own skills, or in a code comment counted exactly as
much as an actual payment integration. The result was a profile that activated modules for
capabilities the project did not have, and an audit that then reported on them.
Every detection now records an evidence class — manifest, implementation, configuration,
route, schema, test, documentation, fixture, generated, example, or unknown —
together with the path and line it came from, a confidence, an activation weight, a reason, and the
workspace it belongs to.
Only production-bearing evidence activates a capability:
- Documentation, tests, fixtures, and generated Forge or platform copies carry zero activation
weight. They are recorded as observed, and they never activate anything. - Examples are separated from active applications and carry a small fraction of a weight.
- Keywords found in comments or passive string literals are downgraded.
UNKNOWN is now a real answer
Capability determination used to be boolean: any signal meant present, no signal meant absent.
Neither could express the most common truth, which is that there was not enough evidence to decide.
Determination now returns PRESENT, ABSENT, or UNKNOWN against an explicit activation
threshold. A pile of weak signals produces UNKNOWN instead of a false PRESENT, and silence
produces UNKNOWN instead of a false ABSENT.
Assessments are computed per workspace and published in .forge/project-profile.json as
capability_assessments. Existing language, framework, and structured discovery is unchanged.
Applicability now reads assessments
Module applicability prefers a capability assessment over the legacy presence map.
capabilityStatusFor projects a CapabilityAssessment onto the v0.1.8 module-decision capability
axis, and decisionFindingStatus remains the canonical mapping to NOT_APPLICABLE and
NOT_VERIFIED.
The projection never strengthens a claim. This matters more than it may sound: v0.1.8 exists
precisely to stop NOT_APPLICABLE from meaning anything other than "this capability provably does
not exist", and projecting an UNKNOWN assessment as ABSENT would have quietly resurrected that
defect. So:
UNKNOWNstaysUNKNOWNand is never reported as a proven absence.- Across a monorepo,
PRESENTin any workspace wins. ABSENTrequires every workspace to prove absence. Absence in one workspace is not absence in
the project.
A capability the evidence layer does not model produces no assessment, and that silence is not read
as evidence. The layer models sixteen capabilities while module decisions are gated on twenty-four;
the other eight continue to use the legacy presence map rather than being reported UNKNOWN
forever.
Specification traceability is published and machine-checked
A public traceability matrix records every authoritative requirement in the maintainers' own words,
with implementation, test, documentation, and release-verification evidence, a status, and honest
limitations. No private specification wording is quoted, reproduced, or referenced.
config/traceability-matrix.json is the source of truth and docs/TRACEABILITY_MATRIX.md is
generated from it. npm run check:traceability runs inside npm run check, and therefore inside
CI, rejecting:
- duplicate or non-dense requirement identifiers
- unsupported statuses
- referenced repository paths that do not exist
COMPLIANTentries with no implementation or no test/documentation evidenceNON_COMPLIANTorNOT_APPLICABLEentries with no stated reasonNOT_VERIFIEDentries that do not distinguish a genuine external limit from unfinished local work
Current matrix: 75 requirements — 37 COMPLIANT, 36 PARTIALLY_COMPLIANT, 2 NOT_VERIFIED,
with zero integration placeholders.
Corrected attributions
The matrix previously carried four integration: placeholders whose attributions were self-declared
inference. All four are replaced with the exact merged files, tests, documentation, and release
evidence, and two were simply wrong:
FF-MOD-15(static security analyzer) belongs to v0.1.7 / PR #19, not v0.1.9.
cli/src/analyzers.tsfirst landed inc8073edon the offline-security branch.FF-ORCH-01(repository-wide orchestrator) belongs to v0.1.8 / PR #20 and v0.1.9 / PR
#21, not v0.1.7.
Every remaining attribution was re-checked against the commit that introduced each referenced file
rather than trusted.
The two remaining NOT_VERIFIED entries are genuinely external: both are GitHub-hosted repository
settings that cannot be proven from repository contents.
Upgrading
.forge/project-profile.json gains a capability_assessments array. Existing fields are unchanged,
and tooling that ignores unknown fields is unaffected. If you have tooling that inferred capability
presence from the legacy capabilities map, note that a capability may now be reported UNKNOWN
where it was previously inferred present from weak evidence.
See the traceability overview and
the release verification record.
Fullstack Forge v0.1.9
Release notes — v0.1.9
Audit orchestration and report-output milestone.
Before this release a forge <section> audit was static-only. Anything that required running a
project command or a browser lived in a separate forge tool invocation whose result never reached
the report. An audit therefore produced a document that was silent about everything it had not
looked at. This release makes one audit invocation coherent: it names every check it intends to
perform, executes only what it is explicitly authorized to execute, and records every check that did
not run together with the reason.
An audit now plans before it acts
A PlannedCheck list is built before anything executes, and it is deterministic: modules are
sorted, project commands follow a fixed allowlist order, and runtime evidence is always last. Two
audits of the same checkout produce the same plan on any machine.
Every planned check reaches exactly one terminal outcome. There is no path on which a check is
planned and then silently forgotten. --json output gains planned_checks, check_outcomes,
runtime_evidence, and evidence_complete.
New options that change behaviour rather than being parsed and ignored:
--check <name>and--skip-check <name>, repeatable, accepting the full check identifier or the
bare name. An unknown value is an error, not a silent no-op — an operator who types--check lnit
must not be told the audit passed with the lint check quietly absent.--url <url>integrates rendered-UI evidence from an application the operator has already
started.--evidence-dir <path>relocates collected runtime evidence beneath the audited root.
Nothing runs that was not authorized
A default audit remains static-only. Project-command execution requires explicit --allow-run and
is restricted to a bounded allowlist of read-only scripts, so start, dev, serve, deploy, and
publish are not merely excluded — they are unreachable. An audit can never become the thing that
starts or exposes the audited system. Commands run as a bounded argument vector, never as a shell
string. Browser tooling is never installed automatically, and Forge never launches an application:
--url requires one that is already running.
Behaviour change: --offline refuses arbitrary project commands
This changes the output of forge <section> audit --offline --allow-run.
An earlier state of this branch decided offline safety by scanning a script's definition for network
keywords. A command containing none — eslint ., vitest run, tsc -p . — was executed under
--offline.
That inference is unsound, and the defect was reproduced before it was fixed. Inspecting text can
prove that a command reaches the network; it can never prove that it does not. Fullstack Forge
implements no operating-system network isolation, so there is nothing to make the guess safe. The
sandbox is, and is reported as, none.
Planned checks now carry a network_policy obtained exclusively through plannedCheckNetworkPolicy
— the single sanctioned bridge into the report vocabulary, introduced in v0.1.8 for exactly this
purpose. The rules:
- Forge-owned scripts matched by exact definition, and proven cache-only installation commands, are
OFFLINE_SAFEand may run. These are the only two structurally provable exemptions. - Every arbitrary audited-project command is
UNKNOWNand is blocked under--offline. - Keyword scanning may escalate
UNKNOWNtoNETWORK_REQUIRED. Nothing may downgrade a command to
OFFLINE_SAFE. - A blocked command never produces PASS gate evidence.
If you relied on --offline --allow-run executing your project's scripts, re-run without
--offline and record that the result was obtained with network access.
Requested evidence fails closed
A rendered capture that comes back PARTIAL, BLOCKED, or FAILED leaves the rendered criteria
NOT_VERIFIED and makes the audit exit 2. Exit 1 still means a proven defect; exit 2 means
nothing failed, but the run did not prove what it was asked to prove. Runtime evidence requested
under --offline is refused rather than attempted, and that refusal also marks the evidence
incomplete.
Not-run is not the same as blocked
A check nobody authorized is recorded NOT_RUN, never BLOCKED. BLOCKED marks a defect whose
remediation is obstructed and feeds the forge fix candidate set; an unauthorized check is not a
defect awaiting remediation, and treating it as one breaks forge all fix --safe --allow-run.
BLOCKED is reserved for the two refusals Forge itself made: offline policy and failed-closed
evidence.
Report output is contained and owned
forge <section> report renders Markdown to stdout, JSON under --json, and writes report.json
plus report.md under --output <directory>; --dry-run prints the planned paths and writes
nothing. Report mode never re-runs an audit, so the rendered document preserves the identity,
revision, timestamps, and evidence of the run it names.
The output directory is resolved beneath the authorized root. Traversal, absolute, drive-qualified,
UNC, and symlinked destinations are refused. Forge records the digest of each file it writes and
refuses to overwrite either an unowned pre-existing file or managed output that was edited after
Forge wrote it. Identical content is preserved rather than rewritten.
Evidence reaches the report through the v0.1.8 ledgers
Orchestration does not write report fields directly. It emits four kinds of fact across an
AuditLedgerSink boundary, and ReportAuditLedger persists them into the v0.1.8 typed
planned_checks, runtime_evidence, and tools ledgers using the append-only cli/src/ledger.ts
API. That API refuses to rewrite a blocked or unverified result as passing, so the honesty invariant
is enforced by the ledger rather than by each caller remembering to be careful.
An executed project command is recorded as a project-owned, untrusted tool with an unknown
version source. Forge did not author it and cannot attest to what it checked.
Upgrading
No report migration is required: the schema is unchanged from v0.1.8. Tooling that parses --json
audit output gains four new top-level fields and should ignore unknown fields as before.
See the CLI reference for the full option and policy documentation, and
the release verification record for the evidence gathered before
this tag was created.
Fullstack Forge v0.1.8
Release notes — v0.1.8
Module applicability and report evidence ledger milestone.
This release closes a family of defects in which an unaudited module was reported in a way that read
as a positive result. Every defect was reproduced against the v0.1.7 implementation before
remediation and is covered by regression tests that fail against it.
NOT_APPLICABLE now means only that a capability does not exist
NOT_APPLICABLE previously absorbed three unrelated situations: a capability that genuinely does
not exist, a module left out of the changed scope, and a module whose capability could not be
determined. Collapsing them lost the distinction that matters most — whether Forge looked.
Applicability is now recorded on two independent axes:
capability_statusanswers "does this capability exist in the project?"selection_statusanswers "did this run audit it?"
NOT_APPLICABLE is reserved for a capability proven absent. A module that exists but was not
audited is NOT_VERIFIED, which is a statement about missing evidence rather than about the
project.
A consequence worth stating plainly: narrowing an audit can no longer switch a release gate off.
Capability ship gates read the capability axis, so a module that is present but unaudited leaves its
gate active instead of being dismissed as inapplicable.
Behaviour change: risk-excluded modules now appear in the report
This changes the output of forge all audit --risk high.
Previously, a module below the risk threshold vanished from the report entirely. Nothing recorded
that it had gone unaudited, so its absence was indistinguishable from having been audited and found
clean. A report is a claim about what was checked, and silently dropping the modules that were not
checked makes that claim misleading.
Excluded modules now appear with:
- status
NOT_VERIFIED— no evidence was gathered, and none is claimed - a module decision with
selection_status: EXCLUDED_BY_RISK - a
capability_statusthat is notABSENT, because a risk filter proves nothing about whether
a capability exists
If you have tooling that reads Forge reports, note the migration: absence is no longer a signal.
Anything that treated a missing module as "passed" must now read module_decisions and check
selection_status. Reports from --risk high runs will contain more entries than before.
This also required changing a pre-existing assertion in cli/tests/cli.test.ts, which asserted that
the ui module was absent from a high-risk report. That assertion encoded the old behaviour. It now
asserts the module is present, NOT_VERIFIED, and EXCLUDED_BY_RISK.
Report schema version 2
The report gains four typed, append-only ledgers: tools, planned_checks, runtime_evidence, and
module_decisions. They are documented in report schema and rendered in both
the JSON and Markdown outputs.
The ledger APIs in cli/src/ledger.ts enforce one invariant throughout: honesty only ever
decreases. A check recorded BLOCKED or NOT_RUN can never later be rewritten as RUN, and
runtime evidence recorded BLOCKED or NOT_VERIFIED can never later be rewritten as PASS.
Re-recording a weaker outcome is allowed, because discovering that a result was less certain than
believed is a legitimate correction; the reverse is not, because the stronger claim was never
observed.
Reports written by v0.1.3 through v0.1.7 migrate in memory. Ledgers the writing release never
tracked come back empty and carry an explicit note that emptiness reflects untracked data — not
evidence that the corresponding checks ran or passed. The source file is never rewritten.
Migration no longer claims a v0.1.7 report was written by v0.1.6. v0.1.7 changed no report field, so
the two are indistinguishable from a report alone; the migration record names both and says why.
The v0.1.7 offline policy is preserved
v0.1.8 introduces a coarser network-policy vocabulary for the report (OFFLINE_SAFE,
NETWORK_REQUIRED, UNKNOWN) alongside the v0.1.7 command vocabulary. Two vocabularies create a
laundering risk: an arbitrary audited-project command that v0.1.7 refuses to guess about could be
re-described as offline-safe on the way into the report.
plannedCheckNetworkPolicy is now the only sanctioned bridge, and the mapping is one-way. The two
structurally provable exemptions become OFFLINE_SAFE; UNKNOWN always stays UNKNOWN. There is
no inverse function and no promotion path. Absence of network keywords is not proof of offline
safety.
This was a latent type-level hazard rather than an observed defect — no production code assigned the
weaker value yet — but the wiring that would exercise it is the subject of the next milestone, so it
is closed now.
Upgrading
No configuration changes are required. If you consume Forge reports programmatically:
- Read
schema_version; it is now2. - Stop treating a missing module as a pass. Read
module_decisions. - Treat an empty ledger as untracked, not as a clean result. Check
migration.absent_ledgers.
Fullstack Forge v0.1.7
Release notes — v0.1.7
Offline command policy and structural security proof milestone.
This release closes two defects in which Fullstack Forge reported a stronger guarantee than it
actually enforced. Both were reproduced against the v0.1.6 implementation before remediation and are
covered by regression tests that fail against it.
--offline is enforced on every command execution path
--offline previously reached only the rendered-UI driver. forge tool run-project-command and
every forge ship gate spawned the audited project's own scripts with unrestricted network access
while the report recorded offline: true — a silently ignored flag and a false offline claim.
Classification now happens in one place and every execution path consults it. The model is
deliberately small and pessimistic:
- An arbitrary audited-project script is classified
UNKNOWNfrom its definition, never its
name.test,lint, orverify:offlinesay nothing about what a script does. - Fullstack Forge implements no operating-system network isolation. No namespace, seccomp,
firewall, or container boundary exists in this codebase, so none is claimed:sandboxis always
reported asnone, andUNKNOWNcommands are blocked offline rather than "sandboxed". - Exactly two exemptions are structurally provable: Forge's own repository scripts, matched by exact
definition and only when the audited root is canonically the Forge package root; and an explicitly
designed cache-only installation check that combines an offline package-manager flag with an
unreachable registry.
Every command now carries a ledger record (RAN, BLOCKED, NOT_RUN) with its reason. A blocked
command produces no execution record and no typed evidence, so it can never satisfy a release gate.
Analyzer protection is granted only from analyzed structure
Protection is no longer granted from a function's name. parse, validate, assertValid,
sanitize, allowlist, assertAllowed, requireAllowed, allowedValue, trusted, and safe
are discovery hints only; a no-op function with any of those names now leaves the SQL, shell, SSRF,
redirect, mass-assignment, upload, and AI findings reported.
SSRF address guards are covered by the same rule. isPrivate, isLinkLocal, isInternal, and
privateAddress were still credited from the callee name alone, so a no-op guard —
function isPrivate(value) { return false; } — suppressed the SSRF finding while blocking nothing,
contradicting the documented claim that no protection is granted from an identifier's name. A guard
is now credited only when a same-file implementation is analyzed: it must accept the value under
test, reference it, and decide against concrete loopback, private, or link-local address evidence. A
constant-returning body proves nothing. Genuine structurally proven address guards continue to
suppress.
Destination maps likewise require strong proof. A const object of URL strings is not an SSRF
defence: http://127.0.0.1:3000/ and http://169.254.169.254/latest/meta-data/ are fixed literals
and exactly the destinations an SSRF attack wants, and const prevents neither
MAP.key = req.query.url nor mutate(MAP). Suppression now requires fixed literal destinations,
demonstrable immutability, no property write or delete, no alias escape, no export or return, no
pass to an unmodelled function, direct flow to the sink, an explicit redirect constraint, absent
credentials, a supported protocol, and classification of literal addresses.
Known limitations
- An address guard imported from another module or a package is not modeled. That mitigation is
reported as unverified rather than credited, so the SSRF finding is raised. This is deliberate: an
unread implementation cannot be proven to block anything. - Hostname destinations are accepted but recorded as DNS-dependent. No resolution is performed, so
DNS rebinding and private A records stay outside the proof. - No operating-system network isolation exists. Offline enforcement is a policy decision about
whether to spawn a command, not a sandbox.
Verification
Local validation evidence gathered before the tag is recorded in
docs/RELEASE_VERIFICATION_v0.1.7.md. Remote CI, publication, provenance, and immutable-release
checks are pending at tag time and are recorded separately after publication.
Fullstack Forge v0.1.6
Fullstack Forge v0.1.6
Rendered-UI security milestone. Three defects were independently reproduced against v0.1.5 before
any remediation was written, and each fix is covered by regression tests that fail against the
previous implementation.
This release completes the rendered-UI security milestone only. It does not close the other deferred
specification areas — report-ledger redesign, changed-scope applicability semantics, discovery
evidence classes, normal-audit orchestration, and the static security analyzer are unchanged and
remain outstanding.
Fixed
Offline mode is enforced across the whole browser, not just the first URL
v0.1.5 validated the initial destination and then called page.goto() with no further checks. Once
a loopback page was open it could still reach the network freely: an HTTP redirect to a public host,
a remote script, stylesheet, font, or image, an iframe, a worker, or any fetch/XHR the page
chose to issue. Each of those performs DNS resolution and opens a connection, so the documented
offline contract — "no DNS resolution or network request was attempted" — was true only of the first
URL.
Rendered inspection now installs a request interceptor before navigating and evaluates every request
against a single network policy. Non-loopback HTTP and HTTPS requests are aborted before they are
sent, which prevents both the connection and the DNS lookup that would precede it. Redirects surface
as fresh requests for the new destination and are blocked the same way. Blocked destinations are
recorded as evidence with their URLs redacted, and any blocked request prevents a COMPLETE
capture, so a page that only rendered because its resources were withheld cannot yield a rendered
PASS.
Loopback classification moved into a shared net-policy module rather than being duplicated as
string comparisons. It covers localhost, *.localhost, the entire 127.0.0.0/8 range, ::1,
IPv4-mapped and IPv4-compatible IPv6 loopback, and trailing-dot and case variants; numeric IPv4
forms are normalized by the URL parser before classification. Private, link-local, unique-local, and
cloud-metadata addresses are classified explicitly and are never treated as loopback.
Two boundaries are stated honestly rather than overclaimed. A browser driver that cannot intercept
requests is refused outright under --offline instead of falling back to the old initial-URL check.
WebSocket handshakes are not covered by HTTP request interception, so a loopback guard is installed
inside the page as an init script; transports outside interception and that guard — WebRTC and
browser-internal telemetry among them — are recorded as NOT_VERIFIED rather than claimed blocked.
Incomplete rendered capture fails closed
v0.1.5 granted a rendered PASS whenever at least one screenshot existed and no console error was
seen, and exited 0 whenever no console error was seen at all. Two outcomes followed directly. One
viewport succeeding and two failing produced a PASS describing "1 viewport screenshot(s)" as
positive rendered-state evidence. Every viewport failing produced zero screenshots, zero findings,
status OK, and exit code 0 — a completely failed inspection reported as a clean run.
Every run now reports an explicit capture_status:
COMPLETE every required viewport captured and hashed, nothing blocked
PARTIAL some evidence collected, but a required step failed or was blocked
BLOCKED a policy boundary prevented execution
FAILED execution was attempted but produced no usable capture
Only COMPLETE with zero console errors may produce the informational FF-UI-RENDER-001 PASS.
COMPLETE with console errors produces the failing FF-UI-CONSOLE-001. Every other status produces
FF-UI-CAPTURE-001 and leaves the rendered criteria NOT_VERIFIED. Each viewport carries its own
status, artifact, hash, and error, and a screenshot call that resolves without writing a readable
artifact is now a failure rather than counted evidence. Partial evidence is preserved rather than
discarded, the manifest and the CLI result always report the same status, and browser cleanup runs
on every launch-success path with close failures recorded instead of swallowed.
Exit codes follow one rule: 0 is reserved for a complete capture with nothing failing, 1 marks a
run with a failing finding or a runtime failure, and 2 marks a run whose evidence is merely
absent.
All rendered evidence is redacted before it is written
v0.1.5 redacted query values in the source URL but wrote console text, page errors, and navigation
failures verbatim into console.json, into finding evidence, and into limitations. Those strings
are application-controlled and routinely carry bearer tokens, session cookies, API keys, full URLs
with credentials, and absolute paths naming the operator's account.
One shared redaction layer now processes every string before it is written, printed, or turned into
a finding. It removes URL userinfo, query values, and fragments; authorization, cookie, session, and
API-key assignments across =, :, and quoted-JSON syntaxes; JWT-shaped values and vendor-prefixed
keys; residual high-entropy credential runs; and home-directory paths on both Windows and POSIX.
Output is length-bounded and states whether it was redacted, truncated, or both, so a shortened
string is never presented as whole. Redaction is deliberately structural rather than a list of known
secrets. SHA-256 digests are preserved because they are evidence rather than credentials, and
ordinary diagnostics such as net::ERR_CONNECTION_REFUSED remain fully readable.
A double browser close on the offline refusal path
Refusing a non-intercepting driver under --offline closed the browser explicitly and then closed
it again in the cleanup block. Found by the new regression tests and fixed; cleanup now runs exactly
once.
Compatibility
RenderedUiResult gains capture_status, viewports, and blocked_requests; the rendered-UI
evidence manifest moves to schema_version: 2 with the same additions. This is the tool's own
evidence manifest, not the audit report schema — report loading and migration for v0.1.3, v0.1.4,
and v0.1.5 are untouched. No public report schema migration is included in this release. Playwright
and browser binaries remain optional; the implementation is exercised in tests through controlled
fake browser objects.
Verification
Local validation covers formatting, lint, type checking, the full test suite, coverage thresholds,
validation, platform synchronization, links, licenses, fixtures, workflow policy, release documents,
install documents, branding, secret scanning, deterministic packaging of all nine platform archives,
dist validation, smoke and offline installation, and npm audit. Rendered-state criteria stay
NOT_VERIFIED wherever no trusted browser driver is available, which is the expected outcome for
this repository's own CI. See docs/RELEASE_VERIFICATION_v0.1.6.md for the verification record and
its pending remote steps.
Fullstack Forge v0.1.5
Fullstack Forge v0.1.5
Trust-boundary and honest-evidence patch. Four defects were independently reproduced against v0.1.4
before any remediation was written, and each fix is covered by a regression test that fails against
the previous implementation.
Fixed
--offline is now enforced, not merely parsed
--offline was accepted, documented, and stored, but no code path ever read it: audits behaved
identically with and without the flag. The flag now carries a defined contract. Under --offline
Fullstack Forge refuses HTTP and HTTPS requests to any non-loopback destination before DNS
resolution is attempted, refuses to resolve a browser driver from the audited project (because
resolution can trigger installation or a registry lookup), and reports network-dependent checks as
BLOCKED with the criteria they would have covered left NOT_VERIFIED. It never reports PASS for
work it did not do. Offline state is recorded in the report environment ledger and in every rendered
evidence manifest. Static analysis, local report generation, local verification, installation from
bundled assets, and loopback UI inspection all remain fully available offline.
Audited-project code is no longer executed without authorization
Rendered-UI inspection resolved Playwright from the audited project and dynamically imported it.
Importing a package executes its top-level JavaScript, so a hostile repository shipping a fake
playwright package could run arbitrary code inside the auditor's process. Two paths made this
worse than documented: the import happened for loopback URLs with no --allow-run at all, and it
happened before the --dry-run check, so even a planning-only run executed the audited project's
code.
Browser drivers are now resolved from the Fullstack Forge package root first. The audited project's
copy is used only under explicit --allow-run, only after the resolved real path is proven to lie
inside the audited repository — which defeats symlink, path-escape, and module-redirection tricks —
and never under --offline. --dry-run resolves, imports, and launches nothing. The resolved
package, version, real path, trust domain, and trusted flag are recorded in evidence.
SSRF protection requires structural proof, not a reassuring name
The analyzer granted trusted-origin and network-constrained protection whenever a callee name
matched mapDestination, trustedDestination, resolveAllowedDestination, or similar, and treated
any map whose text mentioned allowed/trusted/destinations/routes — or was simply uppercase —
as a server-owned allowlist. A one-line no-op named mapDestination therefore suppressed a real
SSRF finding, as did const ALLOWED_DESTINATIONS = req.body.
No protection is granted from an identifier's name any more. Destination proof now requires a
const declaration initialized with an object literal whose every value is a fixed absolute http(s)
URL literal and which never received request-controlled data, or a dominating guard bound to the
same tainted value that reaches the sink. Redirect control remains required. Anything the bounded
engine cannot prove stays request-controlled rather than assumed safe.
Rendered evidence is preserved per route and per run
Every rendered-UI invocation wrote desktop.png, tablet.png, mobile.png, and console.json to
one fixed directory, so inspecting a second route destroyed the first route's evidence and
re-running a route destroyed its own history. Evidence is now written to
.forge/evidence/ui/<revision>/<run-id>/<route-id>/. The route identifier combines a sanitized
single path segment with a hash of the normalized URL, so query strings, fragments, and traversal
sequences cannot influence the directory layout. URL credentials are rejected outright and query
values are redacted before reaching any artifact or directory name. Manifests record the revision,
timestamp, redacted source and final URL, redirect state, viewport dimensions, driver identity and
version, per-screenshot SHA-256 hashes, console-output hash, authorization state, offline state, and
any limitations. A partial browser failure preserves the honest partial evidence and always closes
the browser.
Added
- An
environmentledger in the report schema recording operating system, platform, architecture,
Node version, Forge version, offline mode, and execution authorization. The field is optional, so
v0.1.3 and v0.1.4 reports continue to load and render; a legacy report states that the record is
absent rather than having plausible values back-filled into it. npm run check:install-docs, which fails the build when documentation presents the unpublished
npm registry installation as a command that works today.
Changed
- The README now leads with the Git-tag installation that actually resolves. The npm registry form
is retained only under an explicit "after npm registry publication" heading and marked
NOT YET AVAILABLE; the package is not on the registry and no registry publication is claimed. docs/CLI_REFERENCE.mddocuments the offline contract, the browser-driver trust rules, and the
rendered-evidence layout.docs/SECURITY_MODEL.mdrecords the corresponding controls.
Installation
npm install --save-dev github:thethunderbolt/fullstack-forge-skill#v0.1.5
npx forge init all --dry-run
npx forge init allKnown limitations
Analysis remains bounded and intra-file; unsupported languages and framework shapes stay
NOT_VERIFIED. Rendered-state criteria stay NOT_VERIFIED wherever no trusted browser driver is
available, which is the expected outcome for this repository's own CI. See
docs/RELEASE_VERIFICATION_v0.1.5.md for the verification record and its pending remote steps.
Fullstack Forge v0.1.4
Fullstack Forge v0.1.4
v0.1.4 is a security-correctness and release-integrity patch. It makes taint protection
sink-specific, makes authorization proof structural, completes instance-specific fix lifecycle
handling, replaces broad ship-gate inference with typed evidence, removes scanner fixtures from the
dependency graph, and makes future releases non-clobbering and immutable.
Classification record: docs/AUDIT_CLASSIFICATION_v0.1.4.md.
Tagged local record: docs/RELEASE_VERIFICATION_v0.1.4.md. Final
remote verification is pending and will be published by the tag workflow as
FINAL_RELEASE_VERIFICATION_v0.1.4.md plus its checksum. That final asset is generated after the
tag and is not represented as content of tagged source.
Analyzer correctness
- Generic parse, validation, normalization, escaping, or encoding no longer clears taint. Values
retain their origin and carry typed protection evidence such as parameterized, shell-separated,
allowlisted, trusted-origin, or network-constrained. - SQL parameter binding and fixed executable/validated argument arrays are recognized only for the
corresponding sink class. SSRF allowlists remain tied to the actual destination, require redirect
control, and are invalidated by later raw reassignment. - Authorization requires a final owner/tenant predicate or a dominating guard connected to both
authenticated subject and object. Policy words, comments, unused imports, later calls, and guards
for another object are not evidence. - The bounded analyzer still does not claim whole-program soundness.
Instance-specific evidence and safe fixes
Finding instances now derive from rule, path, containing declaration, receiver, sink, normalized AST
shape, and deterministic same-scope occurrence. Exact identity flows through analyzer verification,
safe-fix planning and writes, blocked attempts, public JSON, Markdown, ship evidence, report
updates, and rollback. v0.1.3 reports without instance-specific fields remain readable.
Fix and verification transitions recompute report revision, retain prior typed gate and coverage
evidence, and attach ROLLED_BACK attempts only to the affected instance. Structurally identical
peers retain their identity when a sibling becomes safe.
The public JSON Schema was also corrected to describe the runtime instance_id, fix_attempts, and
scoped analyzer action fields.
Exact ship gates and adapter coverage
Ship gates accept only their declared evidence types and require current revision/timestamp
evidence. A SQL or SSRF result cannot masquerade as secret scanning; dependency, lockfile, license,
authorization, tenant, upload, migration, test, and release-artifact evidence remain independent.
Normal JSON and Markdown audit output now records analyzer coverage for every detected
language/framework in selected modules. FastAPI and Django are distinguished, unknown Python stays
unknown, and Go, Rust, JVM, mixed-language, and partial JavaScript/TypeScript gaps name the adapter
that would close them.
Dependency and workflow hygiene
- Scanner fixtures are stored as non-installable
package.json.fixturedata and materialized only
inside disposable test projects. A fail-closed fixture validator prevents real manifests,
lockfiles, scripts, symlinks, or non-sentinel versions from returning. - Secret scanning subtracts deleted tracked worktree paths while continuing to scan untracked files,
so manifest renames cannot crash or silently narrow local validation. - Root dependency audit reports no known vulnerability. TypeScript 7 and
@types/node26 remain
intentional major ignores to preserve the supported TypeScript ESLint peer range and Node 24 API
baseline. - GitHub Actions are pinned to reviewed full commit SHAs for checkout v7, setup-node v7,
upload-artifact v7.0.1, dependency review v5, provenance attestation v4.1.1, and CodeQL v4. - A least-privilege no-build CodeQL workflow now scans JavaScript/TypeScript on pull requests,
main, a weekly schedule, and manual dispatch.
Coverage and release integrity
Coverage now fails CI below committed overall and high-risk file floors; statements are explicitly
unmeasured because the Node.js reporter exposes lines, branches, and functions only. Generated
Markdown and package copies are checked by synchronization and deterministic packaging rather than
misrepresented as executable coverage.
The tag workflow:
- proves the annotated tag commit and absence of an existing release;
- runs checks, enforced coverage, dependency audit, packaging, smoke install, and offline install;
- creates one draft and uploads without
--clobber; - attests every initial asset, downloads the exact expected set, and compares archive, checksum,
and manifest bytes; - adds a checksummed and attested final evidence asset;
- publishes exactly once; and
- verifies GitHub immutable release state, asset attestations, and the remote tag commit.
Repository immutable releases are enabled for future publication. Existing tags and releases were
not moved, recreated, or modified.
Install
npm install --save-dev github:thethunderbolt/fullstack-forge-skill#v0.1.4
npx forge init allOr download the agent-specific ZIP from the GitHub release, verify it against SHA256SUMS.txt, and
extract it at the repository root. The release contains nine deterministic ZIP archives,
SHA256SUMS.txt, manifest.json, and the two final evidence assets.
Compatibility and limitations
- Node.js 24 or newer remains required; CLI command and 42-module catalog compatibility is retained.
- Python, Go, Rust, and JVM security/authorization adapters remain
NOT_VERIFIEDgaps. - Cross-file data flow, dynamic properties, reflective calls, middleware inheritance, and production
controls remain outside bounded static proof. - Runtime accessibility, database, backup, malware scanning, provider, deployment, and operator
checks still require direct evidence. - The project is not published to the npm registry; repository/tag and release-archive installation
are the supported publication paths for this release.
Fullstack Forge v0.1.3
Fullstack Forge v0.1.3
Corrective correctness release. An independent audit of v0.1.2 proposed thirteen problem areas; each
was independently reproduced against source before any change was made. Twelve were confirmed
outright, two were narrowed to partially confirmed, and none was accepted on the strength of the
report alone. Every confirmed defect is closed with executable evidence and a regression test.
Classification evidence: docs/AUDIT_CLASSIFICATION_v0.1.3.md.
Verification record: docs/RELEASE_VERIFICATION_v0.1.3.md.
Fixed — report semantics
- A refused fix no longer unproves the defect. When an automatic fix was blocked, the engine
overwrote the finding'sFAILorWARNINGwithBLOCKED, discarding proven evidence. Defect
status, fix-attempt status, and verification status are now distinct: refusal is recorded in a new
fix_attempts[]structure and the defect status is preserved. --safenow means something. The flag was parsed and never read, soforge all fixand
forge all fix --safewere identical and both mutated files. The contract is now explicit:fix
plans only and never writes,fix --safeexecutes bounded safe registry entries, and
fix --safe --dry-runplans without writing.
Fixed — finding identity and verification
- Findings are instance-specific. Multiple occurrences of one rule collapsed into a single
global finding, so fixing one occurrence could not be distinguished from fixing another. Findings
now carry a stableinstance_idderived from the rule, the repository-relative path, and the sink
symbol — deliberately not from line numbers, so unrelated edits do not mint a new identity. - Verification is scoped to the original evidence. Analyzer verification re-ran over the whole
repository and matched on the rule ID, so an unrelated occurrence elsewhere re-failed a resolved
finding. It is now scoped to the original evidence paths and matched on instance identity. - Dry run executes nothing.
forge all verify --dry-run --allow-runstill ran project commands.
A dry run now reports the command it would execute and runs nothing.
Fixed — ship gates
- Gates are classified as
forge-self,audited-application, orproject-native. Previously every
command-backed internal gate was markedNOT_APPLICABLEandrequired: falsefor any project
that was not Fullstack Forge itself, silently disabling secret scanning, dependency inspection,
and license validation for every audited application. Those gates are now required for ordinary
projects and fall back to recorded audit evidence when the project exposes no matching command. - Forge packaging, archive, installer, and platform-synchronisation checks remain self-checks and
are not demanded of audited applications. - Removed an unreachable dependency-evidence branch that could never execute.
Fixed — analyzer data flow
A bounded intra-file taint engine replaces raw-text matching at high-risk sinks. It resolves local
aliases, reassignment, object and array destructuring, template-literal and string-concatenation
propagation, and same-file function-parameter summaries, and it records the source-to-sink path as
trace evidence.
Previously undetected, now detected:
const id = req.params.id;
db.query(`SELECT * FROM users WHERE id = ${id}`); // FF-SEC-SQL-001Sanitizers now bind to the specific value they were applied to. An unrelated validate* call in the
same function no longer suppresses a real finding, and naming a variable "owner policy" no longer
counts as an authorization predicate.
This is explicitly not whole-program sound. Cross-file propagation, dynamic property access, and
reflective call targets are unresolved and reported as such — never inferred as safe.
Fixed — discovery
- Repository confidence now uses
git rev-parse --is-inside-work-tree. The previous check looked
for.git/inside a file walk that excludes.git, so it could never be true. - Workspaces are resolved from declared configuration (
package.jsonworkspaces,
pnpm-workspace.yaml,lerna.json,nx.json,turbo.json). An undeclared nested manifest — a
fixture or example — is reported as a low-confidencenested-package, not an active workspace. - Bounded route adapters added for Next.js App Router, Next.js Pages Router, NestJS decorators, and
Fastify object-form routes. Each record names the adapter that produced it. - Route visibility inferred from a path name (
/login,/health,/public) is now reported at
LOWconfidence and discloses that it is a heuristic.
Fixed — changed scope
Base resolution follows an explicit precedence: --base, branch upstream, origin/HEAD,
origin/main, origin/master, local main, local master. HEAD is never used as a fallback —
merge-base HEAD HEAD is HEAD, which silently hid every committed branch change. When no
meaningful base exists the analysis returns a structured BLOCKED naming what was tried.
Added — analyzer support registry
cli/src/support.ts records, per module and language, the analyzer, coverage level, supported
shapes, and unsupported shapes. Missing coverage now reports the specific adapter required:
NOT_VERIFIED; missing adapter: module=authorization; language=Python;
framework=FastAPI; required adapter=fastapi-authorization-boundaries
Tests
164 tests, all passing, up from 117 at v0.1.2. No existing test was removed or weakened.
Release status
This version is prepared and verified locally. Tag creation, CI across Linux, Windows, and
macOS, release publication, published-asset checksums, and provenance attestations are recorded as
BLOCKED in the verification record and have not been performed.
Fullstack Forge v0.1.2
Fullstack Forge v0.1.2
Fullstack Forge is a production engineering skill suite for AI coding agents. It discovers a real
application stack, selects only applicable modules, gathers reproducible evidence, separates safe
fixes from risky decisions, and refuses to call missing evidence a pass.
This release answers an independent conformance audit of v0.1.1. Every audit finding is closed with
executable evidence rather than documentation changes.
What changed
Authentication detection. The broken-auth fixture contained a session cookie set to a
user-supplied email with httpOnly: false and secure: false, and the auth audit produced no
automated finding. Two analyzers now close that gap: FF-AUTH-COOKIE-001 for weakened cookie
attributes and FF-AUTH-SESSION-001 for request-derived session identifiers.
Broader security coverage. Four analyzers were added with dedicated fixture cases: SSRF
(FF-SEC-SSRF-001), unsafe deserialization and code evaluation (FF-SEC-DESERIALIZE-001), CSV
formula injection (FF-SEC-CSV-001), and mass assignment (FF-SEC-MASS-ASSIGN-001).
Rendered-UI evidence. A new 25th tool, inspect-rendered-ui, captures desktop, tablet, and
mobile screenshots plus browser console output into .forge/evidence/ui/ using the audited
project's own Playwright installation. It is deliberately conservative: it never launches project
servers, requires an explicit URL, refuses non-loopback destinations without --allow-run, and
reports BLOCKED when Playwright is absent so rendered-state criteria stay NOT_VERIFIED rather
than being fabricated. Console errors produce a failing FF-UI-CONSOLE-001 finding.
Discipline-specific procedures. All 42 modules previously shared one generic eight-step
inspection procedure. Each module now carries its own ordered steps — 212 in total — wrapped by a
shared scope opening and evidence closing. forge-database reads schema, types, cascades, and
migration history; forge-realtime traces connect authorization, channel isolation, reconnection,
and backpressure.
Verification depth. 26 of 32 evaluation cases now assert a stable automated finding ID, up from
9 of 26. The suite grew from 96 to 117 tests, with 88.8% line coverage.
Supply chain and release integrity. CI adds macOS, fails on stale committed build/ output,
reports coverage, and verifies offline installation. Release archives now carry build-provenance
attestations. The release workflow derives its notes path from the tag instead of hard-coding a
version, and workflow-level write permission was replaced with job-scoped tokens.
Provenance. research/LICENSE_MATRIX.md now records a measured text-overlap comparison: zero
shared eight-word sequences across 1,967 upstream Markdown files and 766,216 shingles from all
eleven researched repositories. This matters most for Trail of Bits (CC BY-SA 4.0), where adapted
prose would impose share-alike terms incompatible with the Apache-2.0 core.
Supported platforms
Claude Code (.claude/skills/), OpenAI Codex (.agents/skills/), Google Antigravity
(.agents/skills/ project, ~/.gemini/config/skills/ global), Gemini CLI (.gemini/skills/),
Cursor (.cursor/skills/), Windsurf/Devin Cascade (.windsurf/skills/), GitHub Copilot
(.github/skills/), and generic Agent Skills clients. Paths and their primary sources are recorded
in docs/PLATFORM_SUPPORT.md.
Installation
npm install --save-dev github:thethunderbolt/fullstack-forge-skill#v0.1.2
npx forge init all --dry-run
npx forge init allOr download the archive for your agent below, verify it against SHA256SUMS.txt, and extract it at
the project root. Archives contain real file copies and no symlinks.
Main commands
forge discover audit
forge security audit --json
forge uploads audit
forge all audit --scope changed --base origin/main
forge all fix --safe
forge ship --allow-run
forge tool inspect-rendered-ui http://127.0.0.1:3000/ --jsonEvidence model
Findings carry id, section, title, severity, confidence, status, location, evidence,
impact, recommendation, safe_fix, verification, and standards. Statuses are PASS,
FAIL, WARNING, NOT_APPLICABLE, NOT_VERIFIED, and BLOCKED.
A pass requires direct evidence: code with file and line references, a successful automated check, a
running-application inspection, a behavior-demonstrating test, or verified configuration output.
Static analyzers never emit PASS — absence of an observed defect is not a pass.
Safety model
Audit is read-only. Project commands execute only after their local definitions are shown and
--allow-run is supplied. Safe fixes are bound to a confirmed finding, an exact post-audit file
hash, a structural parser, and a repository-contained path. Uninstall removes only unchanged files
recorded in its own ownership manifest. Installation refuses symlinked destinations, path traversal,
Windows device names, and modified owned files.
Distribution files
Nine platform archives, SHA256SUMS.txt, and manifest.json. Every archive ships LICENSE,
NOTICE, and THIRD_PARTY_NOTICES.md. The antigravity, codex, and generic archives are
byte-identical because all three install to the documented .agents/skills destination.
Known limitations
- Analyzers are bounded to JavaScript and TypeScript. Other stacks report
NOT_VERIFIEDwith the
missing adapter named. - Parts of the security catalogue — CSRF, CRLF and header injection, template injection, request
smuggling, ReDoS, prototype pollution — remain inspection criteria routed to agent-led manual
tracing rather than dedicated analyzers. inspect-rendered-uiproves a route renders and reports console errors. It does not judge visual
design, and it requires the audited project to install Playwright itself.- Remote CI, registry, deployment, and production state require separate direct evidence.
- The social preview image must be uploaded manually; see
docs/RELEASING.md.
Attribution
Original work under Apache-2.0. Public standards (OWASP, NIST, W3C, IETF) and open-source Agent
Skills repositories were studied for concepts and interoperability facts; no third-party code,
prose, data, or branding is included. Provenance is recorded in research/SOURCES.md and
research/LICENSE_MATRIX.md. This project is independent and is not endorsed by OpenAI, Anthropic,
Google, Cursor, Windsurf/Devin, GitHub, OWASP, NIST, or W3C.